diff --git a/README.md b/README.md index bc00c5c8d..778391f32 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -Build: [![Circle CI](https://circleci.com/gh/eclipse/hawkbit.svg?style=svg)](https://circleci.com/gh/eclipse/hawkbit) - # Eclipse.IoT hawkBit - Update Server [hawkBit](https://projects.eclipse.org/projects/iot.hawkbit) is an domain independent back end solution for rolling out software updates to constrained edge devices as well as more powerful controllers and gateways connected to IP based networking infrastructure. +Build: [![Circle CI](https://circleci.com/gh/eclipse/hawkbit.svg?style=shield)](https://circleci.com/gh/eclipse/hawkbit) +[![SonarQuality](https://sonar.eu-gb.mybluemix.net/api/badges/gate?key=org.eclipse.hawkbit:hawkbit-parent)](https://sonar.eu-gb.mybluemix.net) + # Documentation see [hawkBit Wiki](https://github.com/eclipse/hawkbit/wiki) diff --git a/examples/hawkbit-custom-theme-example/src/main/java/org/eclipse/hawkbit/app/MyUI.java b/examples/hawkbit-custom-theme-example/src/main/java/org/eclipse/hawkbit/app/MyUI.java index 5ef632d10..964994317 100644 --- a/examples/hawkbit-custom-theme-example/src/main/java/org/eclipse/hawkbit/app/MyUI.java +++ b/examples/hawkbit-custom-theme-example/src/main/java/org/eclipse/hawkbit/app/MyUI.java @@ -8,6 +8,8 @@ package org.eclipse.hawkbit.app; * http://www.eclipse.org/legal/epl-v10.html */ +import java.util.concurrent.ScheduledExecutorService; + import org.eclipse.hawkbit.ui.HawkbitUI; import org.eclipse.hawkbit.ui.UIEventProvider; import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy; @@ -38,8 +40,8 @@ public class MyUI extends HawkbitUI { private static final long serialVersionUID = 1L; @Autowired - public MyUI(final EventBus systemEventBus, final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, - final UIEventProvider provider) { - super(new DelayedEventBusPushStrategy(eventBus, systemEventBus, provider)); + public MyUI(final ScheduledExecutorService executorService, final EventBus systemEventBus, + final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, final UIEventProvider provider) { + super(new DelayedEventBusPushStrategy(executorService, eventBus, systemEventBus, provider)); } } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java index 01a9231e3..e73244247 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/DeviceSimulatorUpdater.java @@ -34,7 +34,6 @@ 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.Artifact.UrlProtocol; import org.eclipse.hawkbit.dmf.json.model.SoftwareModule; import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol; import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus; @@ -206,12 +205,12 @@ public class DeviceSimulatorUpdater { private static void handleArtifacts(final String targetToken, final List status, final Artifact artifact) { - if (artifact.getUrls().containsKey(UrlProtocol.HTTPS)) { - status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTPS), targetToken, - artifact.getHashes().getSha1(), artifact.getSize())); - } else if (artifact.getUrls().containsKey(UrlProtocol.HTTP)) { - status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTP), targetToken, - artifact.getHashes().getSha1(), artifact.getSize())); + if (artifact.getUrls().containsKey("https")) { + status.add(downloadUrl(artifact.getUrls().get("https"), targetToken, artifact.getHashes().getSha1(), + artifact.getSize())); + } else if (artifact.getUrls().containsKey("http")) { + status.add(downloadUrl(artifact.getUrls().get("http"), targetToken, artifact.getHashes().getSha1(), + artifact.getSize())); } } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java index 5e9b3e049..6a8826c82 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java @@ -11,7 +11,6 @@ package org.eclipse.hawkbit.simulator.amqp; import static org.eclipse.hawkbit.simulator.amqp.AmqpProperties.CONFIGURATION_PREFIX; import java.time.Duration; -import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; @@ -36,6 +35,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.support.RetryTemplate; +import com.google.common.collect.Maps; + /** * The spring AMQP configuration to use a AMQP for communication with SP update * server. @@ -200,13 +201,13 @@ public class AmqpConfiguration { } private Map getDeadLetterExchangeArgs() { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(1); args.put("x-dead-letter-exchange", amqpProperties.getDeadLetterExchange()); return args; } private static Map getTTLMaxArgs() { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(2); args.put("x-message-ttl", Duration.ofDays(1).toMillis()); args.put("x-max-length", 100_000); return args; diff --git a/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java b/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java index e5bc7535d..0316b68e5 100644 --- a/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java +++ b/examples/hawkbit-example-app/src/main/java/org/eclipse/hawkbit/app/MyUI.java @@ -8,6 +8,8 @@ */ package org.eclipse.hawkbit.app; +import java.util.concurrent.ScheduledExecutorService; + import org.eclipse.hawkbit.ui.HawkbitUI; import org.eclipse.hawkbit.ui.UIEventProvider; import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy; @@ -37,8 +39,8 @@ public class MyUI extends HawkbitUI { private static final long serialVersionUID = 1L; @Autowired - public MyUI(final EventBus systemEventBus, final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, - final UIEventProvider provider) { - super(new DelayedEventBusPushStrategy(eventBus, systemEventBus, provider)); + public MyUI(final ScheduledExecutorService scheduledExecutorService, final EventBus systemEventBus, + final org.vaadin.spring.events.EventBus.SessionEventBus eventBus, final UIEventProvider provider) { + super(new DelayedEventBusPushStrategy(scheduledExecutorService, eventBus, systemEventBus, provider)); } } diff --git a/examples/hawkbit-example-app/src/main/resources/application-cloudsandbox.properties b/examples/hawkbit-example-app/src/main/resources/application-cloudsandbox.properties index a0cad4e9b..85c47cb8f 100644 --- a/examples/hawkbit-example-app/src/main/resources/application-cloudsandbox.properties +++ b/examples/hawkbit-example-app/src/main/resources/application-cloudsandbox.properties @@ -9,8 +9,15 @@ 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 \ No newline at end of file +## Configuration for building download URLs - START +hawkbit.artifact.url.protocols.download-http.rel=download-http +hawkbit.artifact.url.protocols.download-http.protocol=https +hawkbit.artifact.url.protocols.download-http.supports=DMF,DDI +hawkbit.artifact.url.protocols.download-http.hostname=hawkbit.eu-gb.mybluemix.net +hawkbit.artifact.url.protocols.download-http.ref={protocol}://{hostname}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} +hawkbit.artifact.url.protocols.md5sum-http.rel=md5sum-http +hawkbit.artifact.url.protocols.md5sum-http.protocol=${hawkbit.artifact.url.protocols.download-http.protocol} +hawkbit.artifact.url.protocols.md5sum-http.supports=DDI +hawkbit.artifact.url.protocols.md5sum-http.hostname=${hawkbit.artifact.url.protocols.download-http.hostname} +hawkbit.artifact.url.protocols.md5sum-http.ref=${hawkbit.artifact.url.protocols.download-http.ref}.MD5SUM +## Configuration for building download URLs - END diff --git a/examples/hawkbit-example-app/src/main/resources/application.properties b/examples/hawkbit-example-app/src/main/resources/application.properties index 6e05b6b5a..0c32c8f8c 100644 --- a/examples/hawkbit-example-app/src/main/resources/application.properties +++ b/examples/hawkbit-example-app/src/main/resources/application.properties @@ -7,17 +7,15 @@ # http://www.eclipse.org/legal/epl-v10.html # +# User Security +security.user.name=admin +security.user.password=admin + # DDI authentication configuration hawkbit.server.ddi.security.authentication.anonymous.enabled=true hawkbit.server.ddi.security.authentication.targettoken.enabled=true hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=true -# 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 diff --git a/examples/hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java b/examples/hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java index 7807d0f11..b8aee0f97 100644 --- a/examples/hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java +++ b/examples/hawkbit-example-mgmt-feign-client/src/main/java/org/eclipse/hawkbit/mgmt/client/resource/builder/SoftwareModuleTypeBuilder.java @@ -50,11 +50,21 @@ public class SoftwareModuleTypeBuilder { return this; } + /** + * @param description + * of the module + * @return the builder itself + */ public SoftwareModuleTypeBuilder description(final String description) { this.description = description; return this; } + /** + * @param maxAssignments + * of a module of that type to the same distribution set + * @return the builder itself + */ public SoftwareModuleTypeBuilder maxAssignments(final int maxAssignments) { this.maxAssignments = maxAssignments; return this; @@ -99,4 +109,4 @@ public class SoftwareModuleTypeBuilder { return body; } -} \ No newline at end of file +} diff --git a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java b/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java index eabd2b329..4795ba7e7 100644 --- a/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java +++ b/hawkbit-artifact-repository-mongo/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactStore.java @@ -227,7 +227,7 @@ public class ArtifactStore implements ArtifactRepository { * @return a paged list of artifacts mapped from the given dbFiles */ private List map(final List dbFiles) { - return dbFiles.stream().map(this::map).collect(Collectors.toList()); + return dbFiles.stream().map(ArtifactStore::map).collect(Collectors.toList()); } /** @@ -263,7 +263,7 @@ public class ArtifactStore implements ArtifactRepository { * the mongoDB gridFs file. * @return a mapped artifact from the given dbFile */ - private GridFsArtifact map(final GridFSFile fsFile) { + private static GridFsArtifact map(final GridFSFile fsFile) { if (fsFile == null) { return null; } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/CacheAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/CacheAutoConfiguration.java index a170dfcd3..2d61624cf 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/CacheAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/CacheAutoConfiguration.java @@ -82,8 +82,7 @@ public class CacheAutoConfiguration extends CachingConfigurerSupport { */ @Override public Collection resolveCaches(final CacheOperationInvocationContext context) { - return super.resolveCaches(context).stream().map(cache -> new TenantCacheWrapper(cache)) - .collect(Collectors.toList()); + return super.resolveCaches(context).stream().map(TenantCacheWrapper::new).collect(Collectors.toList()); } /* diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementConfiguration.java index fc979fd67..15a9c5bcc 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementConfiguration.java @@ -13,7 +13,9 @@ import java.util.ArrayList; import org.eclipse.hawkbit.im.authentication.MultitenancyIndicator; import org.eclipse.hawkbit.im.authentication.PermissionUtils; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -34,6 +36,9 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager; @ConditionalOnMissingBean(UserDetailsService.class) public class InMemoryUserManagementConfiguration extends GlobalAuthenticationConfigurerAdapter { + @Autowired + private SecurityProperties securityProperties; + @Override public void configure(final AuthenticationManagerBuilder auth) throws Exception { final DaoAuthenticationProvider userDaoAuthenticationProvider = new TenantDaoAuthenticationProvider(); @@ -49,7 +54,8 @@ public class InMemoryUserManagementConfiguration extends GlobalAuthenticationCon public UserDetailsService userDetailsService() { final InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(new ArrayList<>()); inMemoryUserDetailsManager.setAuthenticationManager(null); - inMemoryUserDetailsManager.createUser(new User("admin", "admin", PermissionUtils.createAllAuthorityList())); + inMemoryUserDetailsManager.createUser(new User(securityProperties.getUser().getName(), + securityProperties.getUser().getPassword(), PermissionUtils.createAllAuthorityList())); return inMemoryUserDetailsManager; } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java index 9a7c7df52..565b1520a 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java @@ -148,7 +148,7 @@ public class SecurityManagedConfiguration { private DdiSecurityProperties ddiSecurityConfiguration; @Autowired - private org.springframework.boot.autoconfigure.security.SecurityProperties springSecurityProperties; + private SecurityProperties springSecurityProperties; @Autowired private SystemSecurityContext systemSecurityContext; @@ -478,7 +478,7 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends public void onAuthenticationSuccess(final Authentication authentication) throws Exception { if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) { - systemSecurityContext.runAsSystemAsTenant(() -> systemManagement.getTenantMetadata(), + systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, ((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString()); } else if (authentication.getClass().equals(UsernamePasswordAuthenticationToken.class)) { // TODO: vaadin4spring-ext-security does not give us the @@ -489,7 +489,7 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends // vaadin4spring 0.0.7 because it // has been fixed. final String defaultTenant = "DEFAULT"; - systemSecurityContext.runAsSystemAsTenant(() -> systemManagement.getTenantMetadata(), defaultTenant); + systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, defaultTenant); } super.onAuthenticationSuccess(authentication); @@ -526,7 +526,7 @@ class AuthenticationSuccessTenantMetadataCreationFilter implements Filter { private void lazyCreateTenantMetadata() { final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.isAuthenticated()) { - systemSecurityContext.runAsSystem(() -> systemManagement.getTenantMetadata()); + systemSecurityContext.runAsSystem(systemManagement::getTenantMetadata); } } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java index 0bd4a8240..e95a0ec35 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/url/PropertyHostnameResolverAutoConfiguration.java @@ -12,8 +12,10 @@ import java.net.MalformedURLException; import java.net.URL; import org.eclipse.hawkbit.HawkbitServerProperties; +import org.eclipse.hawkbit.api.ArtifactUrlHandler; +import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties; import org.eclipse.hawkbit.api.HostnameResolver; -import org.springframework.beans.factory.annotation.Autowired; +import org.eclipse.hawkbit.api.PropertyBasedArtifactUrlHandler; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -22,35 +24,41 @@ import org.springframework.context.annotation.Configuration; import com.google.common.base.Throwables; /** - * Autoconfiguration of the {@link HostnameResolver} based on a property. - * - * - * + * Auto configuration for {@link HostnameResolver} and + * {@link ArtifactUrlHandler} based on a properties. */ @Configuration -@EnableConfigurationProperties(HawkbitServerProperties.class) +@EnableConfigurationProperties({ HawkbitServerProperties.class, ArtifactUrlHandlerProperties.class }) public class PropertyHostnameResolverAutoConfiguration { - @Autowired - private HawkbitServerProperties serverProperties; - /** + * @param serverProperties + * to get the servers URL * @return the default autoconfigure hostname resolver implementation which * is property based specified by the property {@link #url} */ @Bean @ConditionalOnMissingBean(value = HostnameResolver.class) - public HostnameResolver hostnameResolver() { - return new HostnameResolver() { - @Override - public URL resolveHostname() { - try { - return new URL(serverProperties.getUrl()); - } catch (final MalformedURLException e) { - throw Throwables.propagate(e); - } + public HostnameResolver hostnameResolver(final HawkbitServerProperties serverProperties) { + return () -> { + try { + return new URL(serverProperties.getUrl()); + } catch (final MalformedURLException e) { + throw Throwables.propagate(e); } }; } + /** + * @param urlHandlerProperties + * for bean configuration + * @return PropertyBasedArtifactUrlHandler bean + */ + @Bean + @ConditionalOnMissingBean(ArtifactUrlHandler.class) + public PropertyBasedArtifactUrlHandler propertyBasedArtifactUrlHandler( + final ArtifactUrlHandlerProperties urlHandlerProperties) { + return new PropertyBasedArtifactUrlHandler(urlHandlerProperties); + } + } diff --git a/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties b/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties index cb7793168..3ac45a027 100644 --- a/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties +++ b/hawkbit-autoconfigure/src/main/resources/hawkbitdefaults.properties @@ -10,6 +10,10 @@ # Displayed basic auth realm security.basic.realm=HawkBit +# User Security +security.user.name=admin +security.user.password=admin + # JPA / Datasource spring.jpa.eclipselink.eclipselink.weaving=false spring.jpa.database=H2 @@ -37,9 +41,25 @@ hawkbit.controller.maxPollingTime=23:59:59 hawkbit.controller.minPollingTime=00:00:30 # Attention: if you want to use a maximumPollingTime greater 23:59:59 you have to update the DurationField in the configuration window - # Configuration for RabbitMQ integration hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter_ttl hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver hawkbit.dmf.rabbitmq.authenticationReceiverQueue=authentication_receiver + +# Download URL generation configuration +hawkbit.artifact.url.protocols.download-http.rel=download-http +hawkbit.artifact.url.protocols.download-http.hostname=localhost +hawkbit.artifact.url.protocols.download-http.ip=127.0.0.1 +hawkbit.artifact.url.protocols.download-http.protocol=http +hawkbit.artifact.url.protocols.download-http.port=8080 +hawkbit.artifact.url.protocols.download-http.supports=DMF,DDI +hawkbit.artifact.url.protocols.download-http.ref={protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} +hawkbit.artifact.url.protocols.md5sum-http.rel=md5sum-http +hawkbit.artifact.url.protocols.md5sum-http.protocol=${hawkbit.artifact.url.protocols.download-http.protocol} +hawkbit.artifact.url.protocols.md5sum-http.hostname=${hawkbit.artifact.url.protocols.download-http.hostname} +hawkbit.artifact.url.protocols.md5sum-http.ip=${hawkbit.artifact.url.protocols.download-http.ip} +hawkbit.artifact.url.protocols.md5sum-http.port=${hawkbit.artifact.url.protocols.download-http.port} +hawkbit.artifact.url.protocols.md5sum-http.supports=DDI +hawkbit.artifact.url.protocols.md5sum-http.ref=${hawkbit.artifact.url.protocols.download-http.ref}.MD5SUM + diff --git a/hawkbit-core/pom.xml b/hawkbit-core/pom.xml index f9e140d40..b8f339a01 100644 --- a/hawkbit-core/pom.xml +++ b/hawkbit-core/pom.xml @@ -33,6 +33,16 @@ guava + + org.easytesting + fest-assert-core + test + + + org.easytesting + fest-assert + test + org.springframework.boot spring-boot-starter-test diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/UrlProtocol.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ApiType.java similarity index 64% rename from hawkbit-core/src/main/java/org/eclipse/hawkbit/api/UrlProtocol.java rename to hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ApiType.java index 77c23ad0d..986bef476 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/UrlProtocol.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ApiType.java @@ -9,8 +9,18 @@ package org.eclipse.hawkbit.api; /** - * Represented the supported protocols for artifact url's. + * hawkBit API type. + * */ -public enum UrlProtocol { - COAP, HTTP, HTTPS +public enum ApiType { + + /** + * Support for Device Management Federation API. + */ + DMF, + + /** + * Support for Direct Device Integration API. + */ + DDI; } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrl.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrl.java new file mode 100644 index 000000000..6d60d01e6 --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrl.java @@ -0,0 +1,109 @@ +/** + * 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.api; + +/** + * Container for a generated Artifact URL. + * + */ +public class ArtifactUrl { + + private final String protocol; + private final String rel; + private final String ref; + + /** + * Constructor. + * + * @param protocol + * string, e.g. ftp, http, https + * @param rel + * hypermedia value + * @param ref + * hypermedia value + */ + public ArtifactUrl(final String protocol, final String rel, final String ref) { + this.protocol = protocol; + this.rel = rel; + this.ref = ref; + } + + /** + * @return protocol name used in DMF API messages. + */ + public String getProtocol() { + return protocol; + } + + /** + * @return rel links value useful in hypermedia. + */ + public String getRel() { + return rel; + } + + /** + * @return generated artifact download URL + */ + public String getRef() { + return ref; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((protocol == null) ? 0 : protocol.hashCode()); + result = prime * result + ((ref == null) ? 0 : ref.hashCode()); + result = prime * result + ((rel == null) ? 0 : rel.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ArtifactUrl other = (ArtifactUrl) obj; + if (protocol == null) { + if (other.protocol != null) { + return false; + } + } else if (!protocol.equals(other.protocol)) { + return false; + } + if (ref == null) { + if (other.ref != null) { + return false; + } + } else if (!ref.equals(other.ref)) { + return false; + } + if (rel == null) { + if (other.rel != null) { + return false; + } + } else if (!rel.equals(other.rel)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "ArtifactUrl [protocol=" + protocol + ", rel=" + rel + ", ref=" + ref + "]"; + } + +} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandler.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandler.java index 03492122c..8f2036c82 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandler.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandler.java @@ -8,36 +8,26 @@ */ package org.eclipse.hawkbit.api; +import java.util.List; + /** * Interface declaration of the {@link ArtifactUrlHandler} which generates the * URLs to specific artifacts. * */ +@FunctionalInterface public interface ArtifactUrlHandler { /** * Returns a generated download URL for a given artifact parameters for a * specific protocol. * - * @param controllerId - * the authenticated controller id - * @param softwareModuleId - * the softwareModuleId belonging to the artifact - * @param filename - * the filename of the artifact - * @param sha1Hash - * the sha1Hash of the artifact - * @param protocol - * the protocol the URL should be generated + * @param placeholder + * data for URL generation + * @param api + * given protocol that URL needs to support + * * @return an URL for the given artifact parameters in a given protocol */ - String getUrl(String controllerId, final Long softwareModuleId, final String filename, final String sha1Hash, - final UrlProtocol protocol); - - /** - * @param protocol - * to check support for - * @return true of the handler supports given protocol. - */ - boolean protocolSupported(UrlProtocol protocol); + List getUrls(URLPlaceholder placeholder, ApiType api); } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandlerProperties.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandlerProperties.java index 490b2d102..86b9e7d79 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandlerProperties.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/ArtifactUrlHandlerProperties.java @@ -8,90 +8,153 @@ */ package org.eclipse.hawkbit.api; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.springframework.boot.context.properties.ConfigurationProperties; +import com.google.common.collect.Lists; + /** * Artifact handler properties class for holding all supported protocols with * host, ip, port and download pattern. + * + * @see PropertyBasedArtifactUrlHandler */ @ConfigurationProperties("hawkbit.artifact.url") public class ArtifactUrlHandlerProperties { - private final Http http = new Http(); - private final Https https = new Https(); - private final Coap coap = new Coap(); - - public Http getHttp() { - return http; - } - - public Https getHttps() { - return https; - } - - public Coap getCoap() { - return coap; - } + /** + * Rel as key and complete protocol as value. + */ + private final Map protocols = new HashMap<>(); /** - * @param protocol - * the protocol schema to retrieve the properties. - * @return the properties to a protocol or {@code null} if protocol does not - * have properties or protocol not supported + * Protocol specific properties to generate URLs accordingly. + * */ - public ProtocolProperties getProperties(final String protocol) { - switch (protocol) { - case "http": - return getHttp(); - case "https": - return getHttps(); - case "coap": - return getCoap(); - default: - return null; - } - } + public static class UrlProtocol { - /** - * Object to hold the properties for the HTTP protocol. - */ - public static class Http extends DefaultProtocolProperties { + private static final int DEFAULT_HTTP_PORT = 8080; /** - * Constructor. + * Set to true if enabled. */ - public Http() { - setPattern( - "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}"); - } - } - - /** - * Object to hold the properties for the HTTP protocol. - */ - public static class Https extends DefaultProtocolProperties { + private boolean enabled = true; /** - * Constructor. + * Hypermedia rel value for this protocol. */ - public Https() { - setPattern( - "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{targetId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}"); - } - } - - /** - * Object to hold the properties for the HTTP protocol. - */ - public static class Coap extends DefaultProtocolProperties { + private String rel = "download-http"; /** - * Constructor. + * Hypermedia ref pattern for this protocol. Supported place holders are + * protocol,controllerId,targetId,targetIdBase62,ip,port,hostname, + * artifactFileName,artifactSHA1, + * artifactIdBase62,artifactId,tenant,softwareModuleId, + * softwareModuleIdBase62. + * + * The update server itself supports */ - public Coap() { - setPattern("{protocol}://{ip}:{port}/fw/{tenant}/{targetId}/sha1/{artifactSHA1}"); - setPort("5683"); + private String ref = "{protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}"; + + /** + * Protocol name placeholder that can be used in ref pattern. + */ + private String protocol = "http"; + + /** + * Hostname placeholder that can be used in ref pattern. + */ + private String hostname = "localhost"; + + /** + * IP address placeholder that can be used in ref pattern. + */ + // Exception squid:S1313 - default only, can be configured + @SuppressWarnings("squid:S1313") + private String ip = "127.0.0.1"; + + /** + * Port placeholder that can be used in ref pattern. + */ + private Integer port = DEFAULT_HTTP_PORT; + + /** + * Support for the following hawkBit API. + */ + private List supports = Lists.newArrayList(ApiType.DDI, ApiType.DMF); + + public boolean isEnabled() { + return enabled; } + + public void setEnabled(final boolean enabled) { + this.enabled = enabled; + } + + public String getRel() { + return rel; + } + + public void setRel(final String rel) { + this.rel = rel; + } + + public String getRef() { + return ref; + } + + public void setRef(final String ref) { + this.ref = ref; + } + + public String getHostname() { + return hostname; + } + + public void setHostname(final String hostname) { + this.hostname = hostname; + } + + public String getIp() { + return ip; + } + + public void setIp(final String ip) { + this.ip = ip; + } + + public Integer getPort() { + return port; + } + + public void setPort(final Integer port) { + this.port = port; + } + + public List getSupports() { + return Collections.unmodifiableList(supports); + } + + public void setSupports(final List supports) { + this.supports = Collections.unmodifiableList(supports); + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(final String protocol) { + this.protocol = protocol; + } + + } + + public Map getProtocols() { + return protocols; } } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/Base62Util.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/Base62Util.java new file mode 100644 index 000000000..37952b6a2 --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/Base62Util.java @@ -0,0 +1,68 @@ +/** + * 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.api; + +/** + * Utility class for Base10 to Base62 conversion and vice versa. Base62 has the + * benefit of being shorter in ASCII representation than Base10. + */ +public final class Base62Util { + private static final String BASE62_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static final int BASE62_BASE = BASE62_ALPHABET.length(); + + private Base62Util() { + // Utility class + } + + /** + * @param base10 + * number + * @return converted number into Base62 ASCII string + */ + public static String fromBase10(final long base10) { + if (base10 == 0) { + return "0"; + } + + long temp = base10; + final StringBuilder sb = new StringBuilder(); + + while (temp > 0) { + temp = fromBase10(temp, sb); + } + return sb.reverse().toString(); + } + + /** + * @param base62 + * number + * @return converted number into Base10 + */ + public static Long toBase10(final String base62) { + return toBase10(new StringBuilder(base62).reverse().toString().toCharArray()); + } + + private static Long fromBase10(final long base10, final StringBuilder sb) { + final int rem = (int) (base10 % BASE62_BASE); + sb.append(BASE62_ALPHABET.charAt(rem)); + return base10 / BASE62_BASE; + } + + private static Long toBase10(final char[] chars) { + long base10 = 0L; + for (int i = chars.length - 1; i >= 0; i--) { + base10 += toBase10(BASE62_ALPHABET.indexOf(chars[i]), i); + } + return base10; + } + + private static int toBase10(final int n, final int pow) { + return n * (int) Math.pow(BASE62_BASE, pow); + } +} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/DefaultProtocolProperties.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/DefaultProtocolProperties.java deleted file mode 100644 index 07eac8db1..000000000 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/DefaultProtocolProperties.java +++ /dev/null @@ -1,79 +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.api; - -/** - * Object to hold the properties for the base protocols. - */ -public class DefaultProtocolProperties implements ProtocolProperties { - // The IP address is not hardcoded. It's the default value, if the IP - // address is not configured. - @SuppressWarnings("squid:S1313") - private static final String DEFAULT_IP_LOCALHOST = "127.0.0.1"; - private static final String LOCALHOST = "localhost"; - - private String hostname = LOCALHOST; - private String ip = DEFAULT_IP_LOCALHOST; - private String port = ""; - /** - * An ant-URL pattern with placeholder to build the URL on. The URL can have - * specific artifact placeholder. - */ - private String pattern; - - /** - * Enables protocol. - */ - private boolean enabled = true; - - @Override - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(final boolean enabled) { - this.enabled = enabled; - } - - @Override - public String getHostname() { - return hostname; - } - - public void setHostname(final String hostname) { - this.hostname = hostname; - } - - @Override - public String getIp() { - return ip; - } - - public void setIp(final String ip) { - this.ip = ip; - } - - @Override - public String getPattern() { - return pattern; - } - - public void setPattern(final String urlPattern) { - this.pattern = urlPattern; - } - - @Override - public String getPort() { - return port; - } - - public void setPort(final String port) { - this.port = port; - } -} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java index 91a271541..229438477 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandler.java @@ -9,55 +9,80 @@ package org.eclipse.hawkbit.api; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.stream.Collectors; -import org.eclipse.hawkbit.tenancy.TenantAware; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.stereotype.Component; +import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties.UrlProtocol; import com.google.common.base.Strings; import com.google.common.net.UrlEscapers; /** * Implementation for ArtifactUrlHandler for creating urls to download resource - * based on pattern. + * based on patterns configured by {@link ArtifactUrlHandlerProperties}. + * + * This mechanism can be used to generate links to arbitrary file hosting + * infrastructure. However, the hawkBit update server supports hosting files as + * well in the following {@link UrlProtocol#getRef()} patterns: + * + * Default: + * {protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/ + * softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} + * + * Default (MD5SUM files): + * {protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/ + * softwaremodules/{softwareModuleId}/artifacts/{artifactFileName}.MD5SUM + * */ -@Component -@EnableConfigurationProperties(ArtifactUrlHandlerProperties.class) public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler { private static final String PROTOCOL_PLACEHOLDER = "protocol"; - private static final String TARGET_ID_PLACEHOLDER = "targetId"; + private static final String CONTROLLER_ID_PLACEHOLDER = "controllerId"; + private static final String TARGET_ID_BASE10_PLACEHOLDER = "targetId"; + private static final String TARGET_ID_BASE62_PLACEHOLDER = "targetIdBase62"; private static final String IP_PLACEHOLDER = "ip"; private static final String PORT_PLACEHOLDER = "port"; private static final String HOSTNAME_PLACEHOLDER = "hostname"; private static final String ARTIFACT_FILENAME_PLACEHOLDER = "artifactFileName"; private static final String ARTIFACT_SHA1_PLACEHOLDER = "artifactSHA1"; + private static final String ARTIFACT_ID_BASE10_PLACEHOLDER = "artifactId"; + private static final String ARTIFACT_ID_BASE62_PLACEHOLDER = "artifactIdBase62"; private static final String TENANT_PLACEHOLDER = "tenant"; - private static final String SOFTWARE_MODULE_ID_PLACDEHOLDER = "softwareModuleId"; + private static final String TENANT_ID_BASE10_PLACEHOLDER = "tenantId"; + private static final String TENANT_ID_BASE62_PLACEHOLDER = "tenantIdBase62"; + private static final String SOFTWARE_MODULE_ID_BASE10_PLACDEHOLDER = "softwareModuleId"; + private static final String SOFTWARE_MODULE_ID_BASE62_PLACDEHOLDER = "softwareModuleIdBase62"; - @Autowired - private ArtifactUrlHandlerProperties urlHandlerProperties; + private final ArtifactUrlHandlerProperties urlHandlerProperties; - @Autowired - private TenantAware tenantAware; + /** + * @param urlHandlerProperties + * for URL generation configuration + */ + public PropertyBasedArtifactUrlHandler(final ArtifactUrlHandlerProperties urlHandlerProperties) { + this.urlHandlerProperties = urlHandlerProperties; + } @Override - public String getUrl(final String targetId, final Long softwareModuleId, final String filename, - final String sha1Hash, final UrlProtocol protocol) { + public List getUrls(final URLPlaceholder placeholder, final ApiType api) { - final String protocolString = protocol.name().toLowerCase(); - final ProtocolProperties properties = urlHandlerProperties.getProperties(protocolString); - if (properties == null || properties.getPattern() == null) { - return null; - } + return urlHandlerProperties.getProtocols().entrySet().stream() + .filter(entry -> entry.getValue().getSupports().contains(api)) + .filter(entry -> entry.getValue().isEnabled()) + .map(entry -> new ArtifactUrl(entry.getValue().getProtocol(), entry.getValue().getRel(), + generateUrl(entry.getValue(), placeholder))) + .collect(Collectors.toList()); + + } + + private static String generateUrl(final UrlProtocol protocol, final URLPlaceholder placeholder) { + final Set> entrySet = getReplaceMap(protocol, placeholder).entrySet(); + + String urlPattern = protocol.getRef(); - String urlPattern = properties.getPattern(); - final Set> entrySet = getReplaceMap(targetId, softwareModuleId, - UrlEscapers.urlFragmentEscaper().escape(filename), sha1Hash, protocolString, properties).entrySet(); for (final Entry entry : entrySet) { if (entry.getKey().equals(PORT_PLACEHOLDER)) { urlPattern = urlPattern.replace(":{" + entry.getKey() + "}", @@ -69,30 +94,29 @@ public class PropertyBasedArtifactUrlHandler implements ArtifactUrlHandler { return urlPattern; } - private Map getReplaceMap(final String targetId, final Long softwareModuleId, final String filename, - final String sha1Hash, final String protocol, final ProtocolProperties properties) { + private static Map getReplaceMap(final UrlProtocol protocol, final URLPlaceholder placeholder) { final Map replaceMap = new HashMap<>(); - replaceMap.put(IP_PLACEHOLDER, properties.getIp()); - replaceMap.put(HOSTNAME_PLACEHOLDER, properties.getHostname()); - replaceMap.put(ARTIFACT_FILENAME_PLACEHOLDER, filename); - replaceMap.put(ARTIFACT_SHA1_PLACEHOLDER, sha1Hash); - replaceMap.put(PROTOCOL_PLACEHOLDER, protocol); - replaceMap.put(PORT_PLACEHOLDER, properties.getPort()); - replaceMap.put(TENANT_PLACEHOLDER, tenantAware.getCurrentTenant()); - replaceMap.put(TARGET_ID_PLACEHOLDER, targetId); - replaceMap.put(SOFTWARE_MODULE_ID_PLACDEHOLDER, String.valueOf(softwareModuleId)); + replaceMap.put(IP_PLACEHOLDER, protocol.getIp()); + replaceMap.put(HOSTNAME_PLACEHOLDER, protocol.getHostname()); + replaceMap.put(ARTIFACT_FILENAME_PLACEHOLDER, + UrlEscapers.urlFragmentEscaper().escape(placeholder.getSoftwareData().getFilename())); + replaceMap.put(ARTIFACT_SHA1_PLACEHOLDER, placeholder.getSoftwareData().getSha1Hash()); + replaceMap.put(PROTOCOL_PLACEHOLDER, protocol.getProtocol()); + replaceMap.put(PORT_PLACEHOLDER, protocol.getPort() == null ? null : String.valueOf(protocol.getPort())); + replaceMap.put(TENANT_PLACEHOLDER, placeholder.getTenant()); + replaceMap.put(TENANT_ID_BASE10_PLACEHOLDER, String.valueOf(placeholder.getTenantId())); + replaceMap.put(TENANT_ID_BASE62_PLACEHOLDER, Base62Util.fromBase10(placeholder.getTenantId())); + replaceMap.put(CONTROLLER_ID_PLACEHOLDER, placeholder.getControllerId()); + replaceMap.put(TARGET_ID_BASE10_PLACEHOLDER, String.valueOf(placeholder.getTargetId())); + replaceMap.put(TARGET_ID_BASE62_PLACEHOLDER, Base62Util.fromBase10(placeholder.getTargetId())); + replaceMap.put(ARTIFACT_ID_BASE62_PLACEHOLDER, + Base62Util.fromBase10(placeholder.getSoftwareData().getArtifactId())); + replaceMap.put(ARTIFACT_ID_BASE10_PLACEHOLDER, String.valueOf(placeholder.getSoftwareData().getArtifactId())); + replaceMap.put(SOFTWARE_MODULE_ID_BASE10_PLACDEHOLDER, + String.valueOf(placeholder.getSoftwareData().getSoftwareModuleId())); + replaceMap.put(SOFTWARE_MODULE_ID_BASE62_PLACDEHOLDER, + Base62Util.fromBase10(placeholder.getSoftwareData().getSoftwareModuleId())); 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(); - } - } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/URLPlaceholder.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/URLPlaceholder.java new file mode 100644 index 000000000..43d51db5c --- /dev/null +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/api/URLPlaceholder.java @@ -0,0 +1,247 @@ +/** + * 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.api; + +/** + * Container for variables available to the {@link ArtifactUrlHandler}. + * + */ +public class URLPlaceholder { + private final String tenant; + private final Long tenantId; + private final String controllerId; + private final Long targetId; + private final SoftwareData softwareData; + + /** + * Constructor. + * + * @param tenant + * of the client + * @param tenantId + * of teh tenant + * @param controllerId + * of the target + * @param targetId + * of the target + * @param softwareData + * information about the artifact and software module that can be + * accessed by the URL. + */ + public URLPlaceholder(final String tenant, final Long tenantId, final String controllerId, final Long targetId, + final SoftwareData softwareData) { + this.tenant = tenant; + this.tenantId = tenantId; + this.controllerId = controllerId; + this.targetId = targetId; + this.softwareData = softwareData; + } + + /** + * Information about the artifact and software module that can be accessed + * by the URL. + * + */ + public static class SoftwareData { + private Long softwareModuleId; + private String filename; + private Long artifactId; + private String sha1Hash; + + /** + * Constructor. + * + * @param softwareModuleId + * of the module the artifact belongs to + * @param filename + * of the artifact + * @param artifactId + * of the artifact + * @param sha1Hash + * of the artifact + */ + public SoftwareData(final Long softwareModuleId, final String filename, final Long artifactId, + final String sha1Hash) { + this.softwareModuleId = softwareModuleId; + this.filename = filename; + this.artifactId = artifactId; + this.sha1Hash = sha1Hash; + } + + public Long getSoftwareModuleId() { + return softwareModuleId; + } + + public void setSoftwareModuleId(final Long softwareModuleId) { + this.softwareModuleId = softwareModuleId; + } + + public String getFilename() { + return filename; + } + + public void setFilename(final String filename) { + this.filename = filename; + } + + public Long getArtifactId() { + return artifactId; + } + + public void setArtifactId(final Long artifactId) { + this.artifactId = artifactId; + } + + public String getSha1Hash() { + return sha1Hash; + } + + public void setSha1Hash(final String sha1Hash) { + this.sha1Hash = sha1Hash; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((artifactId == null) ? 0 : artifactId.hashCode()); + result = prime * result + ((filename == null) ? 0 : filename.hashCode()); + result = prime * result + ((sha1Hash == null) ? 0 : sha1Hash.hashCode()); + result = prime * result + ((softwareModuleId == null) ? 0 : softwareModuleId.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final SoftwareData other = (SoftwareData) obj; + if (artifactId == null) { + if (other.artifactId != null) { + return false; + } + } else if (!artifactId.equals(other.artifactId)) { + return false; + } + if (filename == null) { + if (other.filename != null) { + return false; + } + } else if (!filename.equals(other.filename)) { + return false; + } + if (sha1Hash == null) { + if (other.sha1Hash != null) { + return false; + } + } else if (!sha1Hash.equals(other.sha1Hash)) { + return false; + } + if (softwareModuleId == null) { + if (other.softwareModuleId != null) { + return false; + } + } else if (!softwareModuleId.equals(other.softwareModuleId)) { + return false; + } + return true; + } + + } + + public String getTenant() { + return tenant; + } + + public Long getTenantId() { + return tenantId; + } + + public String getControllerId() { + return controllerId; + } + + public Long getTargetId() { + return targetId; + } + + public SoftwareData getSoftwareData() { + return softwareData; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((controllerId == null) ? 0 : controllerId.hashCode()); + result = prime * result + ((softwareData == null) ? 0 : softwareData.hashCode()); + result = prime * result + ((targetId == null) ? 0 : targetId.hashCode()); + result = prime * result + ((tenant == null) ? 0 : tenant.hashCode()); + result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final URLPlaceholder other = (URLPlaceholder) obj; + if (controllerId == null) { + if (other.controllerId != null) { + return false; + } + } else if (!controllerId.equals(other.controllerId)) { + return false; + } + if (softwareData == null) { + if (other.softwareData != null) { + return false; + } + } else if (!softwareData.equals(other.softwareData)) { + return false; + } + if (targetId == null) { + if (other.targetId != null) { + return false; + } + } else if (!targetId.equals(other.targetId)) { + return false; + } + if (tenant == null) { + if (other.tenant != null) { + return false; + } + } else if (!tenant.equals(other.tenant)) { + return false; + } + if (tenantId == null) { + if (other.tenantId != null) { + return false; + } + } else if (!tenantId.equals(other.tenantId)) { + return false; + } + return true; + } + +} diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java index d68e963be..d5995da59 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationKey.java @@ -90,7 +90,7 @@ public enum TenantConfigurationKey { * @param validator * Validator which validates, that property is of correct format */ - private TenantConfigurationKey(final String key, final String defaultKeyName, final Class dataType, + TenantConfigurationKey(final String key, final String defaultKeyName, final Class dataType, final String defaultValue, final Class validator) { this.keyName = key; this.dataType = dataType; diff --git a/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/Base62UtilTest.java b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/Base62UtilTest.java new file mode 100644 index 000000000..c9c11d10c --- /dev/null +++ b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/Base62UtilTest.java @@ -0,0 +1,40 @@ +/** + * 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.api; + +import static org.fest.assertions.api.Assertions.assertThat; + +import org.junit.Test; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +@Features("Unit Tests - Artifact URL Handler") +@Stories("Base62 Utility tests") +public class Base62UtilTest { + + @Test + @Description("Convert Base10 numbres to Base62 ASCII strings.") + public void fromBase10() { + assertThat(Base62Util.fromBase10(0L)).isEqualTo("0"); + assertThat(Base62Util.fromBase10(11L)).isEqualTo("B"); + assertThat(Base62Util.fromBase10(36L)).isEqualTo("a"); + assertThat(Base62Util.fromBase10(999L)).isEqualTo("G7"); + } + + @Test + @Description("Convert Base62 ASCII strings to Base10 numbers.") + public void toBase10() { + assertThat(Base62Util.toBase10("0")).isEqualTo(0L); + assertThat(Base62Util.toBase10("B")).isEqualTo(11); + assertThat(Base62Util.toBase10("a")).isEqualTo(36L); + assertThat(Base62Util.toBase10("G7")).isEqualTo(999L); + } +} diff --git a/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java new file mode 100644 index 000000000..3d7fc18af --- /dev/null +++ b/hawkbit-core/src/test/java/org/eclipse/hawkbit/api/PropertyBasedArtifactUrlHandlerTest.java @@ -0,0 +1,124 @@ +/** + * 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.api; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties.UrlProtocol; +import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import com.google.common.collect.Lists; + +import ru.yandex.qatools.allure.annotations.Description; +import ru.yandex.qatools.allure.annotations.Features; +import ru.yandex.qatools.allure.annotations.Stories; + +/** + * Tests for creating urls to download artifacts. + */ +@Features("Unit Tests - Artifact URL Handler") +@Stories("Test to generate the artifact download URL") +@RunWith(MockitoJUnitRunner.class) +public class PropertyBasedArtifactUrlHandlerTest { + + private static final String TEST_PROTO = "coap"; + private static final String TEST_REL = "download-udp"; + + private static final long TENANT_ID = 456789L; + private static final String CONTROLLER_ID = "Test"; + private static final String FILENAME = "Afile1234"; + private static final long SOFTWAREMODULEID = 87654L; + private static final long TARGETID = 3474366L; + private static final String TARGETID_BASE62 = "EZqA"; + private static final String SHA1HASH = "test12345"; + private static final long ARTIFACTID = 1345678L; + private static final String ARTIFACTID_BASE62 = "5e4U"; + private static final String TENANT = "TEST_TENANT"; + + private static final String HTTP_LOCALHOST = "http://localhost:8080/"; + + private ArtifactUrlHandler urlHandlerUnderTest; + + private ArtifactUrlHandlerProperties properties; + + private static URLPlaceholder placeholder = new URLPlaceholder(TENANT, TENANT_ID, CONTROLLER_ID, TARGETID, + new SoftwareData(SOFTWAREMODULEID, FILENAME, ARTIFACTID, SHA1HASH)); + + @Before + public void setup() { + properties = new ArtifactUrlHandlerProperties(); + urlHandlerUnderTest = new PropertyBasedArtifactUrlHandler(properties); + + } + + @Test + @Description("Tests the generation of http download url.") + public void urlGenerationWithDefaultConfiguration() { + properties.getProtocols().put("download-http", new UrlProtocol()); + + final List ddiUrls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI); + assertEquals( + Lists.newArrayList(new ArtifactUrl("http", "download-http", HTTP_LOCALHOST + TENANT + "/controller/v1/" + + CONTROLLER_ID + "/softwaremodules/" + SOFTWAREMODULEID + "/artifacts/" + FILENAME)), + ddiUrls); + + final List dmfUrls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF); + assertEquals(ddiUrls, dmfUrls); + } + + @Test + @Description("Tests the generation of custom download url with a CoAP example that supports DMF only.") + public void urlGenerationWithCustomConfiguration() { + final UrlProtocol proto = new UrlProtocol(); + proto.setIp("127.0.0.1"); + proto.setPort(5683); + proto.setProtocol(TEST_PROTO); + proto.setRel(TEST_REL); + proto.setSupports(Lists.newArrayList(ApiType.DMF)); + proto.setRef("{protocol}://{ip}:{port}/fw/{tenant}/{controllerId}/sha1/{artifactSHA1}"); + properties.getProtocols().put(TEST_PROTO, proto); + + List urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI); + + assertThat(urls).isEmpty(); + urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF); + + assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO, TEST_REL, + "coap://127.0.0.1:5683/fw/" + TENANT + "/" + CONTROLLER_ID + "/sha1/" + SHA1HASH)), urls); + } + + @Test + @Description("Tests the generation of custom download url using Base62 references with a CoAP example that supports DMF only.") + public void urlGenerationWithCustomShortConfiguration() { + final UrlProtocol proto = new UrlProtocol(); + proto.setIp("127.0.0.1"); + proto.setPort(5683); + proto.setProtocol(TEST_PROTO); + proto.setRel(TEST_REL); + proto.setSupports(Lists.newArrayList(ApiType.DMF)); + proto.setRef("{protocol}://{ip}:{port}/fws/{tenant}/{targetIdBase62}/{artifactIdBase62}"); + properties.getProtocols().put("ftp", proto); + + List urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DDI); + + assertThat(urls).isEmpty(); + urls = urlHandlerUnderTest.getUrls(placeholder, ApiType.DMF); + + assertEquals(Lists.newArrayList(new ArtifactUrl(TEST_PROTO, TEST_REL, + TEST_PROTO + "://127.0.0.1:5683/fws/" + TENANT + "/" + TARGETID_BASE62 + "/" + ARTIFACTID_BASE62)), + urls); + } +} diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiCancelActionToStop.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiCancelActionToStop.java index 3ab8d6b55..45932ff25 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiCancelActionToStop.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiCancelActionToStop.java @@ -25,7 +25,6 @@ public class DdiCancelActionToStop { * ID of the action to be stoppedW */ public DdiCancelActionToStop(final String stopId) { - super(); this.stopId = stopId; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java index cf146b597..8f1172c39 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiChunk.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.ddi.json.model; +import java.util.Collections; import java.util.List; import javax.validation.constraints.NotNull; @@ -65,7 +66,11 @@ public class DdiChunk { } public List getArtifacts() { - return artifacts; + if (artifacts == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(artifacts); } } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiConfig.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiConfig.java index 0dfed6e79..d01b9f277 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiConfig.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiConfig.java @@ -30,7 +30,6 @@ public class DdiConfig { * configuration of the SP target */ public DdiConfig(final DdiPolling polling) { - super(); this.polling = polling; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiDeployment.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiDeployment.java index c7e364b36..2f7fd593e 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiDeployment.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiDeployment.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.ddi.json.model; +import java.util.Collections; import java.util.List; import com.fasterxml.jackson.annotation.JsonValue; @@ -41,7 +42,6 @@ public class DdiDeployment { * to handle. */ public DdiDeployment(final HandlingType download, final HandlingType update, final List chunks) { - super(); this.download = download; this.update = update; this.chunks = chunks; @@ -56,7 +56,11 @@ public class DdiDeployment { } public List getChunks() { - return chunks; + if (chunks == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(chunks); } /** @@ -81,7 +85,7 @@ public class DdiDeployment { private String name; - private HandlingType(final String name) { + HandlingType(final String name) { this.name = name; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiResult.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiResult.java index 36f0d134c..6d5085569 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiResult.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiResult.java @@ -71,7 +71,7 @@ public class DdiResult { private String name; - private FinalResult(final String name) { + FinalResult(final String name) { this.name = name; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiStatus.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiStatus.java index cb9b57187..140ba08e0 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiStatus.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiStatus.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.ddi.json.model; +import java.util.Collections; import java.util.List; import javax.validation.constraints.NotNull; @@ -57,7 +58,11 @@ public class DdiStatus { } public List getDetails() { - return details; + if (details == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(details); } /** @@ -98,7 +103,7 @@ public class DdiStatus { private String name; - private ExecutionStatus(final String name) { + ExecutionStatus(final String name) { this.name = name; } diff --git a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java index f67e0591b..432cd7e4a 100644 --- a/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java +++ b/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/rest/api/DdiRootControllerRestApi.java @@ -37,39 +37,45 @@ public interface DdiRootControllerRestApi { /** * Returns all artifacts of a given software module and target. - * - * @param targetid + * + * @param tenant + * of the client + * @param controllerId * of the target that matches to controller id * @param softwareModuleId * of the software module * @return the response */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetid}/softwaremodules/{softwareModuleId}/artifacts", produces = { + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts", produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity> getSoftwareModulesArtifacts( - @PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid, + ResponseEntity> getSoftwareModulesArtifacts(@PathVariable("tenant") final String tenant, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId); /** * Root resource for an individual {@link Target}. * - * @param targetid + * @param tenant + * of the request + * @param controllerId * of the target that matches to controller id * @param request * the HTTP request injected by spring * @return the response */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetid}", produces = { "application/hal+json", + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}", produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) ResponseEntity getControllerBase(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid); + @PathVariable("controllerId") final String controllerId); /** * Handles GET {@link DdiArtifact} download request. This could be full or * partial (as specified by RFC7233 (Range Requests)) download request. * - * @param targetid - * of the related target + * @param tenant + * of the request + * @param controllerId + * of the target * @param softwareModuleId * of the parent software module * @param fileName @@ -83,17 +89,19 @@ public interface DdiRootControllerRestApi { * {@link HttpStatus#OK} or in case of partial download * {@link HttpStatus#PARTIAL_CONTENT}. */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetid}/softwaremodules/{softwareModuleId}/artifacts/{fileName}") + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}") ResponseEntity downloadArtifact(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("fileName") final String fileName); /** * Handles GET {@link DdiArtifact} MD5 checksum file download request. * - * @param targetid - * of the related target + * @param tenant + * of the request + * @param controllerId + * of the target * @param softwareModuleId * of the parent software module * @param fileName @@ -106,18 +114,20 @@ public interface DdiRootControllerRestApi { * @return {@link ResponseEntity} with status {@link HttpStatus#OK} if * successful */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetid}/softwaremodules/{softwareModuleId}/artifacts/{fileName}" + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}" + DdiRestConstants.ARTIFACT_MD5_DWNL_SUFFIX, produces = MediaType.TEXT_PLAIN_VALUE) ResponseEntity downloadArtifactMd5(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("fileName") final String fileName); /** * Resource for software module. * - * @param targetid - * of the target that matches to controller id + * @param tenant + * of the request + * @param controllerId + * of the target * @param actionId * of the {@link DdiDeploymentBase} that matches to active * actions. @@ -129,19 +139,21 @@ public interface DdiRootControllerRestApi { * the HTTP request injected by spring * @return the response */ - @RequestMapping(value = "/{targetid}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity getControllerBasedeploymentAction(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") @NotEmpty final String targetid, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId, @RequestParam(value = "c", required = false, defaultValue = "-1") final int resource); /** * This is the feedback channel for the {@link DdiDeploymentBase} action. * + * @param tenant + * of the client * @param feedback * to provide - * @param targetid + * @param controllerId * of the target that matches to controller id * @param actionId * of the action we have feedback for @@ -150,33 +162,37 @@ public interface DdiRootControllerRestApi { * * @return the response */ - @RequestMapping(value = "/{targetid}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/" + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/" + DdiRestConstants.FEEDBACK, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) - ResponseEntity postBasedeploymentActionFeedback(@PathVariable("tenant") final String tenant, - @Valid final DdiActionFeedback feedback, @PathVariable("targetid") final String targetid, + ResponseEntity postBasedeploymentActionFeedback(@Valid final DdiActionFeedback feedback, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId); /** * This is the feedback channel for the config data action. * + * @param tenant + * of the client * @param configData * as body - * @param targetid + * @param controllerId * to provide data for * @param request * the HTTP request injected by spring * * @return status of the request */ - @RequestMapping(value = "/{targetid}/" + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CONFIG_DATA_ACTION, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) - ResponseEntity putConfigData(@PathVariable("tenant") final String tenant, - @Valid final DdiConfigData configData, @PathVariable("targetid") final String targetid); + ResponseEntity putConfigData(@Valid final DdiConfigData configData, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId); /** * RequestMethod.GET method for the {@link DdiCancel} action. * - * @param targetid + * @param tenant + * of the request + * @param controllerId * ID of the calling target * @param actionId * of the action @@ -185,19 +201,21 @@ public interface DdiRootControllerRestApi { * * @return the {@link DdiCancel} response */ - @RequestMapping(value = "/{targetid}/" + DdiRestConstants.CANCEL_ACTION + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION + "/{actionId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity getControllerCancelAction(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") @NotEmpty final String targetid, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId); /** * RequestMethod.POST method receiving the {@link DdiActionFeedback} from * the target. * + * @param tenant + * of the client * @param feedback * the {@link DdiActionFeedback} from the target. - * @param targetid + * @param controllerId * the ID of the calling target * @param actionId * of the action we have feedback for @@ -207,10 +225,11 @@ public interface DdiRootControllerRestApi { * @return the {@link DdiActionFeedback} response */ - @RequestMapping(value = "/{targetid}/" + DdiRestConstants.CANCEL_ACTION + "/{actionId}/" + @RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION + "/{actionId}/" + DdiRestConstants.FEEDBACK, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) - ResponseEntity postCancelActionFeedback(@PathVariable("tenant") final String tenant, - @Valid final DdiActionFeedback feedback, @PathVariable("targetid") @NotEmpty final String targetid, + ResponseEntity postCancelActionFeedback(@Valid final DdiActionFeedback feedback, + @PathVariable("tenant") final String tenant, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId); } diff --git a/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java b/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java index 726f5de57..0d60eb7a2 100644 --- a/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java +++ b/hawkbit-ddi-dl-api/src/main/java/org/eclipse/hawkbit/ddi/dl/rest/api/DdiDlArtifactStoreControllerRestApi.java @@ -12,7 +12,7 @@ import java.io.InputStream; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.web.bind.annotation.AuthenticationPrincipal; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -27,7 +27,9 @@ public interface DdiDlArtifactStoreControllerRestApi { /** * Handles GET download request. This could be full or partial download * request. - * + * + * @param tenant + * name of the client * @param fileName * to search for * @param targetid @@ -40,12 +42,14 @@ public interface DdiDlArtifactStoreControllerRestApi { @RequestMapping(method = RequestMethod.GET, value = DdiDlRestConstants.ARTIFACT_DOWNLOAD_BY_FILENAME + "/{fileName}") @ResponseBody - public ResponseEntity downloadArtifactByFilename(@PathVariable("tenant") final String tenant, + ResponseEntity downloadArtifactByFilename(@PathVariable("tenant") final String tenant, @PathVariable("fileName") final String fileName, @AuthenticationPrincipal final String targetid); /** * Handles GET MD5 checksum file download request. * + * @param tenant + * name of the client * @param fileName * to search for * @@ -54,7 +58,7 @@ public interface DdiDlArtifactStoreControllerRestApi { @RequestMapping(method = RequestMethod.GET, value = DdiDlRestConstants.ARTIFACT_DOWNLOAD_BY_FILENAME + "/{fileName}" + DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX) @ResponseBody - public ResponseEntity downloadArtifactMD5ByFilename(@PathVariable("tenant") final String tenant, + ResponseEntity downloadArtifactMD5ByFilename(@PathVariable("tenant") final String tenant, @PathVariable("fileName") final String fileName); } diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java index 55be6f58a..a09155739 100644 --- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java +++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java @@ -12,15 +12,16 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; +import org.eclipse.hawkbit.api.ApiType; import org.eclipse.hawkbit.api.ArtifactUrlHandler; -import org.eclipse.hawkbit.api.UrlProtocol; +import org.eclipse.hawkbit.api.URLPlaceholder; +import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData; import org.eclipse.hawkbit.ddi.dl.rest.api.DdiDlRestConstants; import org.eclipse.hawkbit.ddi.json.model.DdiArtifact; import org.eclipse.hawkbit.ddi.json.model.DdiArtifactHash; @@ -29,6 +30,7 @@ import org.eclipse.hawkbit.ddi.json.model.DdiConfig; import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase; import org.eclipse.hawkbit.ddi.json.model.DdiPolling; import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.Target; @@ -46,11 +48,11 @@ public final class DataConversionHelper { } - static List createChunks(final String targetid, final Action uAction, - final ArtifactUrlHandler artifactUrlHandler) { + static List createChunks(final Target target, final Action uAction, + final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement) { return uAction.getDistributionSet().getModules().stream() .map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(), - module.getName(), createArtifacts(targetid, module, artifactUrlHandler))) + module.getName(), createArtifacts(target, module, artifactUrlHandler, systemManagement))) .collect(Collectors.toList()); } @@ -69,43 +71,42 @@ public final class DataConversionHelper { /** * Creates all (rest) artifacts for a given software module. * - * @param targetid - * of the target + * @param target + * for create URLs for * @param module * the software module + * @param artifactUrlHandler + * for creating download URLs + * @param systemManagement + * for access to tenant meta data * @return a list of artifacts or a empty list. Cannot be . */ - public static List createArtifacts(final String targetid, + public static List createArtifacts(final Target target, final org.eclipse.hawkbit.repository.model.SoftwareModule module, - final ArtifactUrlHandler artifactUrlHandler) { - final List files = new ArrayList<>(); + final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement) { - module.getLocalArtifacts() - .forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact))); - return files; + return module.getLocalArtifacts().stream() + .map(artifact -> createArtifact(target, artifactUrlHandler, artifact, systemManagement)) + .collect(Collectors.toList()); } - private static DdiArtifact createArtifact(final String targetid, final ArtifactUrlHandler artifactUrlHandler, - final LocalArtifact artifact) { + private static DdiArtifact createArtifact(final Target target, final ArtifactUrlHandler artifactUrlHandler, + final LocalArtifact artifact, final SystemManagement systemManagement) { final DdiArtifact file = new DdiArtifact(); file.setHashes(new DdiArtifactHash(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(), - artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTP); - file.add(new Link(linkHttp).withRel("download-http")); - file.add(new Link(linkHttp + DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum-http")); - } + artifactUrlHandler + .getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(), + systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(), + new SoftwareData(artifact.getSoftwareModule().getId(), artifact.getFilename(), artifact.getId(), + artifact.getSha1Hash())), + ApiType.DDI) + .forEach(entry -> file.add(new Link(entry.getRef()).withRel(entry.getRel()))); - if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTPS)) { - final String linkHttps = artifactUrlHandler.getUrl(targetid, artifact.getSoftwareModule().getId(), - artifact.getFilename(), artifact.getSha1Hash(), UrlProtocol.HTTPS); - file.add(new Link(linkHttps).withRel("download")); - file.add(new Link(linkHttps + DdiDlRestConstants.ARTIFACT_MD5_DWNL_SUFFIX).withRel("md5sum")); - } return file; + } static DdiControllerBase fromTarget(final Target target, final Optional action, @@ -133,8 +134,8 @@ public final class DataConversionHelper { } if (target.getTargetInfo().isRequestControllerAttributes()) { - result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant()) - .putConfigData(tenantAware.getCurrentTenant(), null, target.getControllerId())) + result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant()).putConfigData(null, + tenantAware.getCurrentTenant(), target.getControllerId())) .withRel(DdiRestConstants.CONFIG_DATA_ACTION)); } return result; diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java index 3feb3068f..d3f57a7f7 100644 --- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java +++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactStoreController.java @@ -35,7 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.web.bind.annotation.AuthenticationPrincipal; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.WebApplicationContext; diff --git a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java index 33d7daf69..7506ddb17 100644 --- a/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java +++ b/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootController.java @@ -33,6 +33,7 @@ import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.SoftwareManagement; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; @@ -88,6 +89,9 @@ public class DdiRootController implements DdiRootControllerRestApi { @Autowired private TenantAware tenantAware; + @Autowired + private SystemManagement systemManagement; + @Autowired private ArtifactUrlHandler artifactUrlHandler; @@ -99,9 +103,12 @@ public class DdiRootController implements DdiRootControllerRestApi { @Override public ResponseEntity> getSoftwareModulesArtifacts( - @PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId) { - LOG.debug("getSoftwareModulesArtifacts({})", targetid); + LOG.debug("getSoftwareModulesArtifacts({})", controllerId); + + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil + .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final SoftwareModule softwareModule = softwareManagement.findSoftwareModuleById(softwareModuleId); @@ -111,20 +118,21 @@ public class DdiRootController implements DdiRootControllerRestApi { } - return new ResponseEntity<>(DataConversionHelper.createArtifacts(targetid, softwareModule, artifactUrlHandler), + return new ResponseEntity<>( + DataConversionHelper.createArtifacts(target, softwareModule, artifactUrlHandler, systemManagement), HttpStatus.OK); } @Override public ResponseEntity getControllerBase(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid) { - LOG.debug("getControllerBase({})", targetid); + @PathVariable("controllerId") final String controllerId) { + LOG.debug("getControllerBase({})", controllerId); - final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(targetid, IpUtil + final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); if (target.getTargetInfo().getUpdateStatus() == TargetUpdateStatus.UNKNOWN) { - LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", targetid); + LOG.debug("target with {} extsisted but was in status UNKNOWN -> REGISTERED)", controllerId); controllerManagement.updateTargetStatus(target.getTargetInfo(), TargetUpdateStatus.REGISTERED, System.currentTimeMillis(), IpUtil.getClientIpFromRequest( requestResponseContextHolder.getHttpServletRequest(), securityProperties)); @@ -138,12 +146,12 @@ public class DdiRootController implements DdiRootControllerRestApi { @Override public ResponseEntity downloadArtifact(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("fileName") final String fileName) { ResponseEntity result; - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); @@ -151,7 +159,12 @@ public class DdiRootController implements DdiRootControllerRestApi { LOG.warn("Softare module with id {} could not be found.", softwareModuleId); result = new ResponseEntity<>(HttpStatus.NOT_FOUND); } else { + + // Exception squid:S3655 - Optional access is checked in checkModule + // subroutine + @SuppressWarnings("squid:S3655") final LocalArtifact artifact = module.getLocalArtifactByFilename(fileName).get(); + final DbArtifact file = artifactManagement.loadLocalArtifactBinary(artifact); final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader("If-Match"); @@ -196,11 +209,14 @@ public class DdiRootController implements DdiRootControllerRestApi { } @Override + // Exception squid:S3655 - Optional access is checked in checkModule + // subroutine + @SuppressWarnings("squid:S3655") public ResponseEntity downloadArtifactMd5(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") final String targetid, + @PathVariable("controllerId") final String controllerId, @PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("fileName") final String fileName) { - controllerManagement.updateLastTargetQuery(targetid, IpUtil + controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); @@ -223,12 +239,12 @@ public class DdiRootController implements DdiRootControllerRestApi { @Override public ResponseEntity getControllerBasedeploymentAction( - @PathVariable("tenant") final String tenant, @PathVariable("targetid") final String targetid, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId, @RequestParam(value = "c", required = false, defaultValue = "-1") final int resource) { - LOG.debug("getControllerBasedeploymentAction({},{})", targetid, resource); + LOG.debug("getControllerBasedeploymentAction({},{})", controllerId, resource); - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final Action action = findActionWithExceptionIfNotFound(actionId); @@ -239,14 +255,15 @@ public class DdiRootController implements DdiRootControllerRestApi { if (!action.isCancelingOrCanceled()) { - final List chunks = DataConversionHelper.createChunks(targetid, action, artifactUrlHandler); + final List chunks = DataConversionHelper.createChunks(target, action, artifactUrlHandler, + systemManagement); final HandlingType handlingType = action.isForce() ? HandlingType.FORCED : HandlingType.ATTEMPT; final DdiDeploymentBase base = new DdiDeploymentBase(Long.toString(action.getId()), new DdiDeployment(handlingType, handlingType, chunks)); - LOG.debug("Found an active UpdateAction for target {}. returning deyploment: {}", targetid, base); + LOG.debug("Found an active UpdateAction for target {}. returning deyploment: {}", controllerId, base); controllerManagement.registerRetrieved(action, RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target retrieved update action and should start now the download."); @@ -258,12 +275,12 @@ public class DdiRootController implements DdiRootControllerRestApi { } @Override - public ResponseEntity postBasedeploymentActionFeedback(@PathVariable("tenant") final String tenant, - @Valid @RequestBody final DdiActionFeedback feedback, @PathVariable("targetid") final String targetid, + public ResponseEntity postBasedeploymentActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId) { - LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", targetid, actionId, feedback); + LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", controllerId, actionId, feedback); - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); if (!actionId.equals(feedback.getId())) { @@ -285,13 +302,14 @@ public class DdiRootController implements DdiRootControllerRestApi { return new ResponseEntity<>(HttpStatus.GONE); } - controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, targetid, feedback.getId(), action)); + controllerManagement + .addUpdateActionStatus(generateUpdateStatus(feedback, controllerId, feedback.getId(), action)); return new ResponseEntity<>(HttpStatus.OK); } - private ActionStatus generateUpdateStatus(final DdiActionFeedback feedback, final String targetid, + private ActionStatus generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId, final Long actionid, final Action action) { final ActionStatus actionStatus = entityFactory.generateActionStatus(); @@ -300,22 +318,22 @@ public class DdiRootController implements DdiRootControllerRestApi { switch (feedback.getStatus().getExecution()) { case CANCELED: - LOG.debug("Controller confirmed cancel (actionid: {}, targetid: {}) as we got {} report.", actionid, - targetid, feedback.getStatus().getExecution()); + LOG.debug("Controller confirmed cancel (actionid: {}, controllerId: {}) as we got {} report.", actionid, + controllerId, feedback.getStatus().getExecution()); actionStatus.setStatus(Status.CANCELED); actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target confirmed cancelation."); break; case REJECTED: - LOG.info("Controller reported internal error (actionid: {}, targetid: {}) as we got {} report.", actionid, - targetid, feedback.getStatus().getExecution()); + LOG.info("Controller reported internal error (actionid: {}, controllerId: {}) as we got {} report.", + actionid, controllerId, feedback.getStatus().getExecution()); actionStatus.setStatus(Status.WARNING); actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target REJECTED update."); break; case CLOSED: - handleClosedUpdateStatus(feedback, targetid, actionid, actionStatus); + handleClosedUpdateStatus(feedback, controllerId, actionid, actionStatus); break; default: - handleDefaultUpdateStatus(feedback, targetid, actionid, actionStatus); + handleDefaultUpdateStatus(feedback, controllerId, actionid, actionStatus); break; } @@ -331,19 +349,19 @@ public class DdiRootController implements DdiRootControllerRestApi { return actionStatus; } - private static void handleDefaultUpdateStatus(final DdiActionFeedback feedback, final String targetid, + private static void handleDefaultUpdateStatus(final DdiActionFeedback feedback, final String controllerId, final Long actionid, final ActionStatus actionStatus) { - LOG.debug("Controller reported intermediate status (actionid: {}, targetid: {}) as we got {} report.", actionid, - targetid, feedback.getStatus().getExecution()); + LOG.debug("Controller reported intermediate status (actionid: {}, controllerId: {}) as we got {} report.", + actionid, controllerId, feedback.getStatus().getExecution()); actionStatus.setStatus(Status.RUNNING); actionStatus.addMessage( RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target reported " + feedback.getStatus().getExecution()); } - private static void handleClosedUpdateStatus(final DdiActionFeedback feedback, final String targetid, + private static void handleClosedUpdateStatus(final DdiActionFeedback feedback, final String controllerId, final Long actionid, final ActionStatus actionStatus) { - LOG.debug("Controller reported closed (actionid: {}, targetid: {}) as we got {} report.", actionid, targetid, - feedback.getStatus().getExecution()); + LOG.debug("Controller reported closed (actionid: {}, controllerId: {}) as we got {} report.", actionid, + controllerId, feedback.getStatus().getExecution()); if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) { actionStatus.setStatus(Status.ERROR); actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target reported CLOSED with ERROR!"); @@ -354,23 +372,23 @@ public class DdiRootController implements DdiRootControllerRestApi { } @Override - public ResponseEntity putConfigData(@PathVariable("tenant") final String tenant, - @Valid @RequestBody final DdiConfigData configData, @PathVariable("targetid") final String targetid) { - controllerManagement.updateLastTargetQuery(targetid, IpUtil + public ResponseEntity putConfigData(@Valid @RequestBody final DdiConfigData configData, + @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId) { + controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); - controllerManagement.updateControllerAttributes(targetid, configData.getData()); + controllerManagement.updateControllerAttributes(controllerId, configData.getData()); return new ResponseEntity<>(HttpStatus.OK); } @Override public ResponseEntity getControllerCancelAction(@PathVariable("tenant") final String tenant, - @PathVariable("targetid") @NotEmpty final String targetid, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId) { - LOG.debug("getControllerCancelAction({})", targetid); + LOG.debug("getControllerCancelAction({})", controllerId); - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); final Action action = findActionWithExceptionIfNotFound(actionId); @@ -383,7 +401,7 @@ public class DdiRootController implements DdiRootControllerRestApi { final DdiCancel cancel = new DdiCancel(String.valueOf(action.getId()), new DdiCancelActionToStop(String.valueOf(action.getId()))); - LOG.debug("Found an active CancelAction for target {}. returning cancel: {}", targetid, cancel); + LOG.debug("Found an active CancelAction for target {}. returning cancel: {}", controllerId, cancel); controllerManagement.registerRetrieved(action, RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target retrieved cancel action and should start now the cancelation."); @@ -395,13 +413,13 @@ public class DdiRootController implements DdiRootControllerRestApi { } @Override - public ResponseEntity postCancelActionFeedback(@PathVariable("tenant") final String tenant, - @Valid @RequestBody final DdiActionFeedback feedback, - @PathVariable("targetid") @NotEmpty final String targetid, + public ResponseEntity postCancelActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback, + @PathVariable("tenant") final String tenant, + @PathVariable("controllerId") @NotEmpty final String controllerId, @PathVariable("actionId") @NotEmpty final Long actionId) { - LOG.debug("provideCancelActionFeedback for target [{}]: {}", targetid, feedback); + LOG.debug("provideCancelActionFeedback for target [{}]: {}", controllerId, feedback); - final Target target = controllerManagement.updateLastTargetQuery(targetid, IpUtil + final Target target = controllerManagement.updateLastTargetQuery(controllerId, IpUtil .getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties)); if (!actionId.equals(feedback.getId())) { @@ -432,13 +450,13 @@ public class DdiRootController implements DdiRootControllerRestApi { switch (feedback.getStatus().getExecution()) { case CANCELED: LOG.error( - "Controller reported cancel for a cancel which is not supported by the server (actionid: {}, targetid: {}) as we got {} report.", + "Controller reported cancel for a cancel which is not supported by the server (actionid: {}, controllerId: {}) as we got {} report.", actionid, target.getControllerId(), feedback.getStatus().getExecution()); actionStatus.setStatus(Status.WARNING); break; case REJECTED: - LOG.info("Controller rejected the cancelation request (too late) (actionid: {}, targetid: {}).", actionid, - target.getControllerId()); + LOG.info("Controller rejected the cancelation request (too late) (actionid: {}, controllerId: {}).", + actionid, target.getControllerId()); actionStatus.setStatus(Status.WARNING); break; case CLOSED: diff --git a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java index f9625dff7..f97d57bc3 100644 --- a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java +++ b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java @@ -61,8 +61,7 @@ import ru.yandex.qatools.allure.annotations.Stories; @Stories("Deployment Action Resource") public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoDB { - private static final String HTTP_LOCALHOST = "http://localhost/"; - private static final String HTTPS_LOCALHOST = "https://localhost/"; + private static final String HTTP_LOCALHOST = "http://localhost:8080/"; @Test() @Description("Ensures that artifacts are not found, when softare module does not exists.") @@ -174,25 +173,14 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect( jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1", contains(artifact.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.MD5SUM"))) - .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() @@ -203,27 +191,17 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD contains("test1.signature"))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.md5", contains(artifactSignature.getMd5Hash()))) + .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1", + contains(artifactSignature.getSha1Hash()))) + .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1", - contains(artifactSignature.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature.MD5SUM"))) - .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1.signature"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() @@ -359,16 +337,18 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect( jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1", contains(artifact.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.MD5SUM"))) + .andExpect( + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", + contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + + "/controller/v1/4712/softwaremodules/" + + findDistributionSetByAction.findFirstModuleByType(osType).getId() + + "/artifacts/test1"))) + .andExpect( + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", + contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + + "/controller/v1/4712/softwaremodules/" + + findDistributionSetByAction.findFirstModuleByType(osType).getId() + + "/artifacts/test1.MD5SUM"))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].size", contains(5 * 1024))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].filename", contains("test1.signature"))) @@ -377,24 +357,14 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect( jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1", contains(artifactSignature.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature.MD5SUM"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1.signature"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() @@ -486,31 +456,22 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].filename", contains("test1"))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.md5", contains(artifact.getMd5Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1", - contains(artifact.getSha1Hash()))) - - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.MD5SUM"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1", + contains(artifact.getSha1Hash()))) + .andExpect( + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1.MD5SUM"))) + .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].size", contains(5 * 1024))) .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].filename", contains("test1.signature"))) @@ -519,25 +480,14 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD .andExpect( jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1", contains(artifactSignature.getSha1Hash()))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature"))) - .andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", - contains(HTTPS_LOCALHOST + tenantAware.getCurrentTenant() - + "/controller/v1/4712/softwaremodules/" - + findDistributionSetByAction.findFirstModuleByType(osType).getId() - + "/artifacts/test1.signature.MD5SUM"))) - .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() + "/artifacts/test1.signature"))) .andExpect( - jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href", + jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/" + findDistributionSetByAction.findFirstModuleByType(osType).getId() diff --git a/hawkbit-ddi-resource/src/test/resources/application-test.properties b/hawkbit-ddi-resource/src/test/resources/application-test.properties new file mode 100644 index 000000000..599441734 --- /dev/null +++ b/hawkbit-ddi-resource/src/test/resources/application-test.properties @@ -0,0 +1,43 @@ +# +# 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 +# + +spring.data.mongodb.uri=mongodb://localhost/spArtifactRepository${random.value} +spring.data.mongodb.port=28017 + +hawkbit.server.ddi.security.authentication.header.enabled=true +hawkbit.server.ddi.security.authentication.gatewaytoken.name=TestToken + +multipart.max-file-size=5MB + +hawkbit.server.security.dos.maxStatusEntriesPerAction=100 + +hawkbit.server.security.dos.maxAttributeEntriesPerTarget=10 + +spring.jpa.database=H2 +spring.datasource.url=jdbc:h2:mem:sp-db;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=sa + +flyway.enabled=true +flyway.sqlMigrationSuffix=${spring.jpa.database}.sql +#spring.jpa.show-sql=true + +# DDI configuration +hawkbit.controller.pollingTime=00:01:00 +hawkbit.controller.pollingOverdueTime=00:01:00 + +hawkbit.artifact.url.protocols[0].rel=download +hawkbit.artifact.url.protocols[0].protocol=http +hawkbit.artifact.url.protocols[0].supports=DMF,DDI +hawkbit.artifact.url.protocols[0].ref={protocol}://{hostname}:{port}/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{artifactFileName} +hawkbit.artifact.url.protocols[1].rel=md5sum +hawkbit.artifact.url.protocols[1].protocol=${hawkbit.artifact.url.protocols[0].protocol} +hawkbit.artifact.url.protocols[1].supports=${hawkbit.artifact.url.protocols[0].supports} +hawkbit.artifact.url.protocols[1].ref=${hawkbit.artifact.url.protocols[0].ref}.MD5SUM \ No newline at end of file diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java new file mode 100644 index 000000000..fc73116f7 --- /dev/null +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpAuthenticationMessageHandler.java @@ -0,0 +1,236 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.amqp; + +import java.net.URISyntaxException; +import java.util.UUID; + +import org.eclipse.hawkbit.api.HostnameResolver; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; +import org.eclipse.hawkbit.cache.DownloadArtifactCache; +import org.eclipse.hawkbit.cache.DownloadType; +import org.eclipse.hawkbit.dmf.json.model.Artifact; +import org.eclipse.hawkbit.dmf.json.model.ArtifactHash; +import org.eclipse.hawkbit.dmf.json.model.DownloadResponse; +import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; +import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; +import org.eclipse.hawkbit.repository.ArtifactManagement; +import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.model.LocalArtifact; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.amqp.AmqpRejectAndDontRequeueException; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.cache.Cache; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.CredentialsExpiredException; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * + * {@link AmqpMessageHandlerService} handles all incoming target authentication + * AMQP messages that can be used by 3rd party CDN services to check if a target + * is permitted to download certain artifact. This is handled by the queue that + * is configured for the property + * hawkbit.dmf.rabbitmq.authenticationReceiverQueue. + * + */ +public class AmqpAuthenticationMessageHandler extends BaseAmqpService { + private static final Logger LOG = LoggerFactory.getLogger(AmqpAuthenticationMessageHandler.class); + + private final AmqpControllerAuthentication authenticationManager; + + private final ArtifactManagement artifactManagement; + + private final Cache cache; + + private final HostnameResolver hostnameResolver; + + private final ControllerManagement controllerManagement; + + /** + * @param rabbitTemplate + * the configured amqp template. + * @param artifactManagement + * for artifact URI generation + * @param cache + * for download Ids + * @param hostnameResolver + * for resolving the host for downloads + * @param authenticationManager + * for target authentication + * @param controllerManagement + * for target repo access + */ + public AmqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate, + final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement, + final Cache cache, final HostnameResolver hostnameResolver, + final ControllerManagement controllerManagement) { + super(rabbitTemplate); + this.authenticationManager = authenticationManager; + this.artifactManagement = artifactManagement; + this.cache = cache; + this.hostnameResolver = hostnameResolver; + this.controllerManagement = controllerManagement; + } + + /** + * Executed on a authentication request. + * + * @param message + * the amqp message + * @return the rpc message back to supplier. + */ + @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory") + public Message onAuthenticationRequest(final Message message) { + checkContentTypeJson(message); + final SecurityContext oldContext = SecurityContextHolder.getContext(); + try { + return handleAuthenticationMessage(message); + } catch (final RuntimeException ex) { + throw new AmqpRejectAndDontRequeueException(ex); + } finally { + SecurityContextHolder.setContext(oldContext); + } + } + + /** + * check action for this download purposes, the method will throw an + * EntityNotFoundException in case the controller is not allowed to download + * this file because it's not assigned to an action and not assigned to this + * controller. Otherwise no controllerId is set = anonymous download + * + * @param secruityToken + * the security token which holds the target ID to check on + * @param localArtifact + * the local artifact to verify if the given target is allowed to + * download this artifact + */ + private void checkIfArtifactIsAssignedToTarget(final TenantSecurityToken secruityToken, + final LocalArtifact localArtifact) { + + if (secruityToken.getControllerId() != null) { + checkByControllerId(localArtifact, secruityToken.getControllerId()); + } else if (secruityToken.getTargetId() != null) { + checkByTargetId(localArtifact, secruityToken.getTargetId()); + } else { + LOG.info("anonymous download no authentication check for artifact {}", localArtifact); + return; + } + + } + + private void checkByTargetId(final LocalArtifact localArtifact, final Long targetId) { + LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}", targetId, + localArtifact); + if (!controllerManagement.hasTargetArtifactAssigned(targetId, localArtifact)) { + LOG.info("target {} tried to download artifact {} which is not assigned to the target", targetId, + localArtifact); + throw new EntityNotFoundException(); + } + LOG.info("download security check for target {} and artifact {} granted", targetId, localArtifact); + } + + private void checkByControllerId(final LocalArtifact localArtifact, final String controllerId) { + LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}", + controllerId, localArtifact); + if (!controllerManagement.hasTargetArtifactAssigned(controllerId, localArtifact)) { + LOG.info("target {} tried to download artifact {} which is not assigned to the target", controllerId, + localArtifact); + throw new EntityNotFoundException(); + } + LOG.info("download security check for target {} and artifact {} granted", controllerId, localArtifact); + } + + private LocalArtifact findLocalArtifactByFileResource(final FileResource fileResource) { + if (fileResource.getSha1() != null) { + return artifactManagement.findFirstLocalArtifactsBySHA1(fileResource.getSha1()); + } else if (fileResource.getFilename() != null) { + return artifactManagement.findLocalArtifactByFilename(fileResource.getFilename()).stream().findFirst() + .orElse(null); + } else if (fileResource.getArtifactId() != null) { + return artifactManagement.findLocalArtifact(fileResource.getArtifactId()); + } else if (fileResource.getSoftwareModuleFilenameResource() != null) { + return artifactManagement + .findByFilenameAndSoftwareModule(fileResource.getSoftwareModuleFilenameResource().getFilename(), + fileResource.getSoftwareModuleFilenameResource().getSoftwareModuleId()) + .stream().findFirst().orElse(null); + } + return null; + } + + private static Artifact convertDbArtifact(final DbArtifact dbArtifact) { + final Artifact artifact = new Artifact(); + artifact.setSize(dbArtifact.getSize()); + final DbArtifactHash dbArtifactHash = dbArtifact.getHashes(); + artifact.setHashes(new ArtifactHash(dbArtifactHash.getSha1(), dbArtifactHash.getMd5())); + return artifact; + } + + private Message handleAuthenticationMessage(final Message message) { + final DownloadResponse authentificationResponse = new DownloadResponse(); + final MessageProperties messageProperties = message.getMessageProperties(); + final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class); + + final FileResource fileResource = secruityToken.getFileResource(); + try { + SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken)); + + final LocalArtifact localArtifact = findLocalArtifactByFileResource(fileResource); + + if (localArtifact == null) { + LOG.info("target {} requested file resource {} which does not exists to download", + secruityToken.getControllerId(), fileResource); + throw new EntityNotFoundException(); + } + + checkIfArtifactIsAssignedToTarget(secruityToken, localArtifact); + + final Artifact artifact = convertDbArtifact(artifactManagement.loadLocalArtifactBinary(localArtifact)); + if (artifact == null) { + throw new EntityNotFoundException(); + } + authentificationResponse.setArtifact(artifact); + final String downloadId = UUID.randomUUID().toString(); + // SHA1 key is set, download by SHA1 + final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1, + localArtifact.getSha1Hash()); + cache.put(downloadId, downloadCache); + authentificationResponse + .setDownloadUrl(UriComponentsBuilder.fromUri(hostnameResolver.resolveHostname().toURI()) + .path("/api/v1/downloadserver/downloadId/").path(downloadId).build().toUriString()); + authentificationResponse.setResponseCode(HttpStatus.OK.value()); + } catch (final BadCredentialsException | AuthenticationServiceException | CredentialsExpiredException e) { + LOG.error("Login failed", e); + authentificationResponse.setResponseCode(HttpStatus.FORBIDDEN.value()); + authentificationResponse.setMessage("Login failed"); + } catch (final URISyntaxException e) { + LOG.error("URI build exception", e); + authentificationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); + authentificationResponse.setMessage("Building download URI failed"); + } catch (final EntityNotFoundException e) { + final String errorMessage = "Artifact for resource " + fileResource + "not found "; + LOG.warn(errorMessage, e); + authentificationResponse.setResponseCode(HttpStatus.NOT_FOUND.value()); + authentificationResponse.setMessage(errorMessage); + } + + return getMessageConverter().toMessage(authentificationResponse, messageProperties); + } + +} diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java index 09edd41d3..24a5025a2 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java @@ -9,14 +9,18 @@ package org.eclipse.hawkbit.amqp; import java.time.Duration; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.hawkbit.api.ArtifactUrlHandler; +import org.eclipse.hawkbit.api.HostnameResolver; +import org.eclipse.hawkbit.cache.CacheConstants; import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; +import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.EntityFactory; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.SystemSecurityContext; @@ -41,14 +45,17 @@ import org.springframework.boot.autoconfigure.amqp.RabbitProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.Cache; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.support.RetryTemplate; import org.springframework.util.ErrorHandler; +import com.google.common.collect.Maps; + /** - * Spring configuration for AMQP 0.9 based DMF communication for indirect device + * Spring configuration for AMQP based DMF communication for indirect device * integration. * */ @@ -251,17 +258,51 @@ public class AmqpConfiguration { } /** - * Create amqp handler service bean. + * Create AMQP handler service bean. * + * @param rabbitTemplate + * for converting messages * @param amqpMessageDispatcherService * to sending events to DMF client + * @param controllerManagement + * for target repo access + * @param entityFactory + * to create entities * * @return handler service bean */ @Bean - public AmqpMessageHandlerService amqpMessageHandlerService( - final AmqpMessageDispatcherService amqpMessageDispatcherService) { - return new AmqpMessageHandlerService(rabbitTemplate(), amqpMessageDispatcherService); + public AmqpMessageHandlerService amqpMessageHandlerService(final RabbitTemplate rabbitTemplate, + final AmqpMessageDispatcherService amqpMessageDispatcherService, + final ControllerManagement controllerManagement, final EntityFactory entityFactory) { + return new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherService, controllerManagement, + entityFactory); + } + + /** + * Create AMQP handler service bean for authentication messages. + * + * @param rabbitTemplate + * for converting messages + * @param authenticationManager + * for target authentication + * @param artifactManagement + * for artifact URI generation + * @param cache + * for download IDs + * @param hostnameResolver + * for resolving the host for downloads + * @param controllerManagement + * for target repo access + * @return handler service bean + */ + @Bean + public AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate, + final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement, + @Qualifier(CacheConstants.DOWNLOAD_ID_CACHE) final Cache cache, final HostnameResolver hostnameResolver, + final ControllerManagement controllerManagement) { + return new AmqpAuthenticationMessageHandler(rabbitTemplate, authenticationManager, artifactManagement, cache, + hostnameResolver, controllerManagement); } /** @@ -291,22 +332,25 @@ public class AmqpConfiguration { @Bean @ConditionalOnMissingBean(AmqpControllerAuthentication.class) - public AmqpControllerAuthentication amqpControllerAuthentication(final ControllerManagement controllerManagement, + public AmqpControllerAuthentication amqpControllerAuthentication(final SystemManagement systemManagement, + final ControllerManagement controllerManagement, final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware, final DdiSecurityProperties ddiSecruityProperties, final SystemSecurityContext systemSecurityContext) { - return new AmqpControllerAuthentication(controllerManagement, tenantConfigurationManagement, tenantAware, - ddiSecruityProperties, systemSecurityContext); + return new AmqpControllerAuthentication(systemManagement, controllerManagement, tenantConfigurationManagement, + tenantAware, ddiSecruityProperties, systemSecurityContext); } @Bean @ConditionalOnMissingBean(AmqpMessageDispatcherService.class) public AmqpMessageDispatcherService amqpMessageDispatcherService(final RabbitTemplate rabbitTemplate, - final AmqpSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler) { - return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler); + final AmqpSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler, + final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement) { + return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler, + systemSecurityContext, systemManagement); } private static Map getTTLMaxArgsAuthenticationQueue() { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(2); args.put("x-message-ttl", Duration.ofSeconds(30).toMillis()); args.put("x-max-length", 1_000); return args; diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java index 2e604aad1..64abd2778 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthentication.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.amqp; -import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; @@ -16,6 +15,7 @@ import javax.annotation.PostConstruct; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.security.ControllerPreAuthenticateSecurityTokenFilter; import org.eclipse.hawkbit.security.ControllerPreAuthenticatedAnonymousDownload; @@ -32,6 +32,8 @@ import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import com.google.common.collect.Lists; + /** * * A controller which handles the DMF AMQP authentication. @@ -42,10 +44,12 @@ public class AmqpControllerAuthentication { private final PreAuthTokenSourceTrustAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthTokenSourceTrustAuthenticationProvider(); - private final List filterChain = new ArrayList<>(); + private List filterChain; private final ControllerManagement controllerManagement; + private final SystemManagement systemManagement; + private final TenantConfigurationManagement tenantConfigurationManagement; private final TenantAware tenantAware; @@ -57,6 +61,7 @@ public class AmqpControllerAuthentication { /** * Constructor. * + * @param systemManagement * @param controllerManagement * @param tenantConfigurationManagement * @param tenantAware @@ -66,10 +71,12 @@ public class AmqpControllerAuthentication { * @param systemSecurityContext * security context */ - public AmqpControllerAuthentication(final ControllerManagement controllerManagement, + public AmqpControllerAuthentication(final SystemManagement systemManagement, + final ControllerManagement controllerManagement, final TenantConfigurationManagement tenantConfigurationManagement, final TenantAware tenantAware, final DdiSecurityProperties ddiSecruityProperties, final SystemSecurityContext systemSecurityContext) { this.controllerManagement = controllerManagement; + this.systemManagement = systemManagement; this.tenantConfigurationManagement = tenantConfigurationManagement; this.tenantAware = tenantAware; this.ddiSecruityProperties = ddiSecruityProperties; @@ -85,6 +92,8 @@ public class AmqpControllerAuthentication { } private void addFilter() { + filterChain = Lists.newArrayListWithExpectedSize(5); + final ControllerPreAuthenticatedGatewaySecurityTokenFilter gatewaySecurityTokenFilter = new ControllerPreAuthenticatedGatewaySecurityTokenFilter( tenantConfigurationManagement, tenantAware, systemSecurityContext); filterChain.add(gatewaySecurityTokenFilter); @@ -106,13 +115,15 @@ public class AmqpControllerAuthentication { } /** - * Performs authentication with the secruity token. + * Performs authentication with the security token. * * @param secruityToken * the authentication request object - * @return the authentfication object + * @return the authentication object */ public Authentication doAuthenticate(final TenantSecurityToken secruityToken) { + resolveTenant(secruityToken); + PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null); for (final PreAuthentificationFilter filter : filterChain) { final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, secruityToken); @@ -126,6 +137,14 @@ public class AmqpControllerAuthentication { } + private void resolveTenant(final TenantSecurityToken securityToken) { + if (securityToken.getTenant() == null) { + securityToken.setTenant(systemSecurityContext + .runAsSystem(() -> systemManagement.getTenantMetadata(securityToken.getTenantId()).getTenant())); + } + + } + private static PreAuthenticatedAuthenticationToken createAuthentication(final PreAuthentificationFilter filter, final TenantSecurityToken secruityToken) { diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java index 7ed5e90c1..5a7e3e083 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpDeadletterProperties.java @@ -9,24 +9,26 @@ package org.eclipse.hawkbit.amqp; import java.time.Duration; -import java.util.HashMap; import java.util.Map; import org.springframework.amqp.core.Queue; import org.springframework.boot.context.properties.ConfigurationProperties; +import com.google.common.collect.Maps; + /** * Bean which holds the necessary properties for configuring the AMQP deadletter * queue. */ @ConfigurationProperties("hawkbit.dmf.rabbitmq.deadLetter") public class AmqpDeadletterProperties { + private static final int THREE_WEEKS = 21; /** * Message time to live (ttl) for the deadletter queue. Default ttl is 3 * weeks. */ - private long ttl = Duration.ofDays(21).toMillis(); + private long ttl = Duration.ofDays(THREE_WEEKS).toMillis(); /** * Return the deadletter arguments. @@ -36,7 +38,7 @@ public class AmqpDeadletterProperties { * @return map which holds the properties */ public Map getDeadLetterExchangeArgs(final String exchange) { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(1); args.put("x-dead-letter-exchange", exchange); return args; } @@ -53,7 +55,7 @@ public class AmqpDeadletterProperties { } private Map getTTLArgs() { - final Map args = new HashMap<>(); + final Map args = Maps.newHashMapWithExpectedSize(1); args.put("x-message-ttl", getTtl()); return args; } diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index 1d24d5846..a4ea97e72 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -14,8 +14,10 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import org.eclipse.hawkbit.api.ApiType; import org.eclipse.hawkbit.api.ArtifactUrlHandler; -import org.eclipse.hawkbit.api.UrlProtocol; +import org.eclipse.hawkbit.api.URLPlaceholder; +import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; @@ -25,8 +27,11 @@ import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest; import org.eclipse.hawkbit.dmf.json.model.SoftwareModule; import org.eclipse.hawkbit.eventbus.EventSubscriber; import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.model.LocalArtifact; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.util.IpUtil; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; @@ -47,6 +52,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { private final ArtifactUrlHandler artifactUrlHandler; private final AmqpSenderService amqpSenderService; + private final SystemSecurityContext systemSecurityContext; + private final SystemManagement systemManagement; /** * Constructor. @@ -57,12 +64,19 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { * to send AMQP message * @param artifactUrlHandler * for generating download URLs + * @param systemSecurityContext + * for execution with system permissions + * @param systemManagement + * to access to tenant metadata */ public AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate, final AmqpSenderService amqpSenderService, - final ArtifactUrlHandler artifactUrlHandler) { + final ArtifactUrlHandler artifactUrlHandler, final SystemSecurityContext systemSecurityContext, + final SystemManagement systemManagement) { super(rabbitTemplate); this.artifactUrlHandler = artifactUrlHandler; this.amqpSenderService = amqpSenderService; + this.systemSecurityContext = systemSecurityContext; + this.systemManagement = systemManagement; } /** @@ -74,20 +88,24 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { */ @Subscribe public void targetAssignDistributionSet(final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent) { - final URI targetAdress = targetAssignDistributionSetEvent.getTargetAdress(); + final URI targetAdress = targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress(); if (!IpUtil.isAmqpUri(targetAdress)) { return; } - final String controllerId = targetAssignDistributionSetEvent.getControllerId(); + final String controllerId = targetAssignDistributionSetEvent.getTarget().getControllerId(); final Collection modules = targetAssignDistributionSetEvent .getSoftwareModules(); final DownloadAndUpdateRequest downloadAndUpdateRequest = new DownloadAndUpdateRequest(); downloadAndUpdateRequest.setActionId(targetAssignDistributionSetEvent.getActionId()); - downloadAndUpdateRequest.setTargetSecurityToken(targetAssignDistributionSetEvent.getTargetToken()); + + final String targetSecurityToken = systemSecurityContext + .runAsSystem(targetAssignDistributionSetEvent.getTarget()::getSecurityToken); + downloadAndUpdateRequest.setTargetSecurityToken(targetSecurityToken); for (final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule : modules) { - final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(controllerId, softwareModule); + final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule( + targetAssignDistributionSetEvent.getTarget(), softwareModule); downloadAndUpdateRequest.addSoftwareModule(amqpSoftwareModule); } @@ -133,51 +151,42 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { return messageProperties; } - private SoftwareModule convertToAmqpSoftwareModule(final String targetId, + private SoftwareModule convertToAmqpSoftwareModule(final Target target, final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule) { final SoftwareModule amqpSoftwareModule = new SoftwareModule(); amqpSoftwareModule.setModuleId(softwareModule.getId()); amqpSoftwareModule.setModuleType(softwareModule.getType().getKey()); amqpSoftwareModule.setModuleVersion(softwareModule.getVersion()); - final List artifacts = convertArtifacts(targetId, softwareModule.getLocalArtifacts()); + final List artifacts = convertArtifacts(target, softwareModule.getLocalArtifacts()); amqpSoftwareModule.setArtifacts(artifacts); return amqpSoftwareModule; } - private List convertArtifacts(final String targetId, final List localArtifacts) { + private List convertArtifacts(final Target target, final List localArtifacts) { if (localArtifacts.isEmpty()) { return Collections.emptyList(); } - return localArtifacts.stream().map(localArtifact -> convertArtifact(targetId, localArtifact)) + return localArtifacts.stream().map(localArtifact -> convertArtifact(target, localArtifact)) .collect(Collectors.toList()); } - private Artifact convertArtifact(final String targetId, final LocalArtifact localArtifact) { + private Artifact convertArtifact(final Target target, final LocalArtifact localArtifact) { final Artifact artifact = new Artifact(); - if (artifactUrlHandler.protocolSupported(UrlProtocol.COAP)) { - artifact.getUrls().put(Artifact.UrlProtocol.COAP, - artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(), - localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.COAP)); - } - - if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTP)) { - artifact.getUrls().put(Artifact.UrlProtocol.HTTP, - artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(), - localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTP)); - } - - if (artifactUrlHandler.protocolSupported(UrlProtocol.HTTPS)) { - artifact.getUrls().put(Artifact.UrlProtocol.HTTPS, - artifactUrlHandler.getUrl(targetId, localArtifact.getSoftwareModule().getId(), - localArtifact.getFilename(), localArtifact.getSha1Hash(), UrlProtocol.HTTPS)); - } + artifact.setUrls(artifactUrlHandler + .getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(), + systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(), + new SoftwareData(localArtifact.getSoftwareModule().getId(), localArtifact.getFilename(), + localArtifact.getId(), localArtifact.getSha1Hash())), + ApiType.DMF) + .stream().collect(Collectors.toMap(e -> e.getProtocol(), e -> e.getRef()))); artifact.setFilename(localArtifact.getFilename()); artifact.setHashes(new ArtifactHash(localArtifact.getSha1Hash(), localArtifact.getMd5Hash())); artifact.setSize(localArtifact.getSize()); return artifact; } + } diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java index e8387e0c2..56ecc84cb 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.amqp; import java.net.URI; -import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; @@ -18,68 +17,45 @@ import java.util.UUID; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import org.eclipse.hawkbit.api.HostnameResolver; -import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; -import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; -import org.eclipse.hawkbit.cache.CacheConstants; -import org.eclipse.hawkbit.cache.DownloadArtifactCache; -import org.eclipse.hawkbit.cache.DownloadType; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus; -import org.eclipse.hawkbit.dmf.json.model.Artifact; -import org.eclipse.hawkbit.dmf.json.model.ArtifactHash; -import org.eclipse.hawkbit.dmf.json.model.DownloadResponse; -import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; -import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; -import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.util.IpUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.cache.Cache; -import org.springframework.http.HttpStatus; import org.springframework.messaging.handler.annotation.Header; import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.web.util.UriComponentsBuilder; /** * - * {@link AmqpMessageHandlerService} handles all incoming AMQP messages for the - * queue which is configure for the property hawkbit.dmf.rabbitmq.receiverQueue. + * {@link AmqpMessageHandlerService} handles all incoming target interaction + * AMQP messages (e.g. create target, check for updates etc.) for the queue + * which is configured for the property hawkbit.dmf.rabbitmq.receiverQueue. * */ public class AmqpMessageHandlerService extends BaseAmqpService { @@ -88,40 +64,29 @@ public class AmqpMessageHandlerService extends BaseAmqpService { private final AmqpMessageDispatcherService amqpMessageDispatcherService; - @Autowired - private ControllerManagement controllerManagement; + private final ControllerManagement controllerManagement; - @Autowired - private AmqpControllerAuthentication authenticationManager; - - @Autowired - private ArtifactManagement artifactManagement; - - @Autowired - @Qualifier(CacheConstants.DOWNLOAD_ID_CACHE) - private Cache cache; - - @Autowired - private HostnameResolver hostnameResolver; - - @Autowired - private EntityFactory entityFactory; - - @Autowired - private SystemSecurityContext systemSecurityContext; + private final EntityFactory entityFactory; /** * Constructor. * - * @param defaultTemplate - * the configured amqp template. + * @param rabbitTemplate + * for converting messages * @param amqpMessageDispatcherService * to sending events to DMF client + * @param controllerManagement + * for target repo access + * @param entityFactory + * to create entities */ - public AmqpMessageHandlerService(final RabbitTemplate defaultTemplate, - final AmqpMessageDispatcherService amqpMessageDispatcherService) { - super(defaultTemplate); + public AmqpMessageHandlerService(final RabbitTemplate rabbitTemplate, + final AmqpMessageDispatcherService amqpMessageDispatcherService, + final ControllerManagement controllerManagement, final EntityFactory entityFactory) { + super(rabbitTemplate); this.amqpMessageDispatcherService = amqpMessageDispatcherService; + this.controllerManagement = controllerManagement; + this.entityFactory = entityFactory; } /** @@ -142,28 +107,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService { return onMessage(message, type, tenant, getRabbitTemplate().getConnectionFactory().getVirtualHost()); } - /** - * Executed on a authentication request. - * - * @param message - * the amqp message - * @return the rpc message back to supplier. - */ - @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory") - public Message onAuthenticationRequest(final Message message) { - checkContentTypeJson(message); - final SecurityContext oldContext = SecurityContextHolder.getContext(); - try { - return handleAuthentifiactionMessage(message); - } catch (final IllegalArgumentException ex) { - throw new AmqpRejectAndDontRequeueException("Invalid message!", ex); - } catch (final TenantNotExistException | TooManyStatusEntriesException e) { - throw new AmqpRejectAndDontRequeueException(e); - } finally { - SecurityContextHolder.setContext(oldContext); - } - } - /** * * Executed if a amqp message arrives. * @@ -206,108 +149,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService { return null; } - private Message handleAuthentifiactionMessage(final Message message) { - final DownloadResponse authentificationResponse = new DownloadResponse(); - final MessageProperties messageProperties = message.getMessageProperties(); - final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class); - final FileResource fileResource = secruityToken.getFileResource(); - try { - SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken)); - - final LocalArtifact localArtifact = findLocalArtifactByFileResource(fileResource); - - if (localArtifact == null) { - LOG.info("target {} requested file resource {} which does not exists to download", - secruityToken.getControllerId(), fileResource); - throw new EntityNotFoundException(); - } - - checkIfArtifactIsAssignedToTarget(secruityToken, localArtifact); - - final Artifact artifact = convertDbArtifact(artifactManagement.loadLocalArtifactBinary(localArtifact)); - if (artifact == null) { - throw new EntityNotFoundException(); - } - authentificationResponse.setArtifact(artifact); - final String downloadId = UUID.randomUUID().toString(); - // SHA1 key is set, download by SHA1 - final DownloadArtifactCache downloadCache = new DownloadArtifactCache(DownloadType.BY_SHA1, - localArtifact.getSha1Hash()); - cache.put(downloadId, downloadCache); - authentificationResponse - .setDownloadUrl(UriComponentsBuilder.fromUri(hostnameResolver.resolveHostname().toURI()) - .path("/api/v1/downloadserver/downloadId/").path(downloadId).build().toUriString()); - authentificationResponse.setResponseCode(HttpStatus.OK.value()); - } catch (final BadCredentialsException | AuthenticationServiceException | CredentialsExpiredException e) { - LOG.error("Login failed", e); - authentificationResponse.setResponseCode(HttpStatus.FORBIDDEN.value()); - authentificationResponse.setMessage("Login failed"); - } catch (final URISyntaxException e) { - LOG.error("URI build exception", e); - authentificationResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); - authentificationResponse.setMessage("Building download URI failed"); - } catch (final EntityNotFoundException e) { - final String errorMessage = "Artifact for resource " + fileResource + "not found "; - LOG.warn(errorMessage, e); - authentificationResponse.setResponseCode(HttpStatus.NOT_FOUND.value()); - authentificationResponse.setMessage(errorMessage); - } - - return getMessageConverter().toMessage(authentificationResponse, messageProperties); - } - - /** - * check action for this download purposes, the method will throw an - * EntityNotFoundException in case the controller is not allowed to download - * this file because it's not assigned to an action and not assigned to this - * controller. Otherwise no controllerId is set = anonymous download - * - * @param secruityToken - * the security token which holds the target ID to check on - * @param localArtifact - * the local artifact to verify if the given target is allowed to - * download this artifact - */ - private void checkIfArtifactIsAssignedToTarget(final TenantSecurityToken secruityToken, - final LocalArtifact localArtifact) { - final String controllerId = secruityToken.getControllerId(); - if (controllerId == null) { - LOG.info("anonymous download no authentication check for artifact {}", localArtifact); - return; - } - LOG.debug("no anonymous download request, doing authentication check for target {} and artifact {}", - controllerId, localArtifact); - if (!controllerManagement.hasTargetArtifactAssigned(controllerId, localArtifact)) { - LOG.info("target {} tried to download artifact {} which is not assigned to the target", controllerId, - localArtifact); - throw new EntityNotFoundException(); - } - LOG.info("download security check for target {} and artifact {} granted", controllerId, localArtifact); - } - - private LocalArtifact findLocalArtifactByFileResource(final FileResource fileResource) { - if (fileResource.getSha1() != null) { - return artifactManagement.findFirstLocalArtifactsBySHA1(fileResource.getSha1()); - } else if (fileResource.getFilename() != null) { - return artifactManagement.findLocalArtifactByFilename(fileResource.getFilename()).stream().findFirst() - .orElse(null); - } else if (fileResource.getSoftwareModuleFilenameResource() != null) { - return artifactManagement - .findByFilenameAndSoftwareModule(fileResource.getSoftwareModuleFilenameResource().getFilename(), - fileResource.getSoftwareModuleFilenameResource().getSoftwareModuleId()) - .stream().findFirst().orElse(null); - } - return null; - } - - private static Artifact convertDbArtifact(final DbArtifact dbArtifact) { - final Artifact artifact = new Artifact(); - artifact.setSize(dbArtifact.getSize()); - final DbArtifactHash dbArtifactHash = dbArtifact.getHashes(); - artifact.setHashes(new ArtifactHash(dbArtifactHash.getSha1(), dbArtifactHash.getMd5())); - return artifact; - } - private static void setSecurityContext(final Authentication authentication) { final SecurityContextImpl securityContextImpl = new SecurityContextImpl(); securityContextImpl.setAuthentication(authentication); @@ -361,10 +202,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService { final DistributionSet distributionSet = action.get().getDistributionSet(); final List softwareModuleList = controllerManagement .findSoftwareModulesByDistributionSet(distributionSet); - final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken()); amqpMessageDispatcherService.targetAssignDistributionSet(new TargetAssignDistributionSetEvent( - target.getOptLockRevision(), target.getTenant(), target.getControllerId(), action.get().getId(), - softwareModuleList, target.getTargetInfo().getAddress(), targetSecurityToken)); + target.getOptLockRevision(), target.getTenant(), target, action.get().getId(), softwareModuleList)); } @@ -481,7 +320,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService { return action; } - private void handleCancelRejected(final Message message, final Action action, final ActionStatus actionStatus) { + private static void handleCancelRejected(final Message message, final Action action, + final ActionStatus actionStatus) { if (action.isCancelingOrCanceled()) { actionStatus.setStatus(Status.WARNING); @@ -495,39 +335,4 @@ public class AmqpMessageHandlerService extends BaseAmqpService { } } - private static void checkContentTypeJson(final Message message) { - final MessageProperties messageProperties = message.getMessageProperties(); - if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) { - return; - } - throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible"); - } - - void setControllerManagement(final ControllerManagement controllerManagement) { - this.controllerManagement = controllerManagement; - } - - void setHostnameResolver(final HostnameResolver hostnameResolver) { - this.hostnameResolver = hostnameResolver; - } - - void setAuthenticationManager(final AmqpControllerAuthentication authenticationManager) { - this.authenticationManager = authenticationManager; - } - - void setArtifactManagement(final ArtifactManagement artifactManagement) { - this.artifactManagement = artifactManagement; - } - - void setCache(final Cache cache) { - this.cache = cache; - } - - void setEntityFactory(final EntityFactory entityFactory) { - this.entityFactory = entityFactory; - } - - void setSystemSecurityContext(final SystemSecurityContext systemSecurityContext) { - this.systemSecurityContext = systemSecurityContext; - } } diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java index b90f8ca46..dd80262a5 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java @@ -20,6 +20,16 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("hawkbit.dmf.rabbitmq") public class AmqpProperties { + private static final int ONE_MINUTE = 60; + + private static final int DEFAULT_QUEUE_DECLARATION_RETRIES = 50; + + private static final int DEFAULT_INITIAL_CONSUMERS = 3; + + private static final int DEFAULT_PREFETCH_COUNT = 10; + + private static final int DEFAULT_MAX_CONSUMERS = 10; + /** * Enable DMF API based on AMQP 0.9 */ @@ -54,24 +64,24 @@ public class AmqpProperties { /** * Requested heartbeat interval from broker in {@link TimeUnit#SECONDS}. */ - private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(60); + private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(ONE_MINUTE); /** * Sets an upper limit to the number of consumers. */ - private int maxConcurrentConsumers = 10; + private int maxConcurrentConsumers = DEFAULT_MAX_CONSUMERS; /** * Tells the broker how many messages to send to each consumer in a single * request. Often this can be set quite high to improve throughput. */ - private int prefetchCount = 10; + private int prefetchCount = DEFAULT_PREFETCH_COUNT; /** * Initial number of consumers. Is scaled up if necessary up to * {@link #maxConcurrentConsumers}. */ - private int initialConcurrentConsumers = 3; + private int initialConcurrentConsumers = DEFAULT_INITIAL_CONSUMERS; /** * The number of retry attempts when passive queue declaration fails. @@ -79,7 +89,7 @@ public class AmqpProperties { * consuming from multiple queues, when not all queues were available during * initialization. */ - private int declarationRetries = 50; + private int declarationRetries = DEFAULT_QUEUE_DECLARATION_RETRIES; public int getDeclarationRetries() { return declarationRetries; diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java index b11d5a437..b0bd1f962 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java @@ -17,6 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.AbstractJavaTypeMapper; import org.springframework.amqp.support.converter.MessageConverter; @@ -39,6 +40,14 @@ public class BaseAmqpService { this.rabbitTemplate = rabbitTemplate; } + protected static void checkContentTypeJson(final Message message) { + final MessageProperties messageProperties = message.getMessageProperties(); + if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) { + return; + } + throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible"); + } + /** * Is needed to convert a incoming message to is originally object type. * @@ -98,7 +107,7 @@ public class BaseAmqpService { return value.toString(); } - protected final void logAndThrowMessageError(final Message message, final String error) { + protected static final void logAndThrowMessageError(final Message message, final String error) { LOGGER.warn("Warning! \"{}\" reported by message: {}", error, message); throw new AmqpRejectAndDontRequeueException(error); } diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java index 9c1265960..c2ac1c35b 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java @@ -16,6 +16,11 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.net.URL; + +import org.eclipse.hawkbit.api.HostnameResolver; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; +import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; import org.eclipse.hawkbit.dmf.json.model.DownloadResponse; @@ -23,8 +28,16 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.jpa.JpaEntityFactory; +import org.eclipse.hawkbit.repository.jpa.model.JpaLocalArtifact; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; +import org.eclipse.hawkbit.repository.model.LocalArtifact; +import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; +import org.eclipse.hawkbit.repository.model.TenantMetaData; import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.DdiSecurityProperties.Authentication.Anonymous; import org.eclipse.hawkbit.security.DdiSecurityProperties.Rp; @@ -33,11 +46,15 @@ import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.cache.Cache; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; @@ -54,15 +71,44 @@ import ru.yandex.qatools.allure.annotations.Stories; */ @Features("Component Tests - Device Management Federation API") @Stories("AmqpController Authentication Test") +@RunWith(MockitoJUnitRunner.class) public class AmqpControllerAuthenticationTest { + private static final String SHA1 = "12345"; + private static final Long ARTIFACT_ID = 1123L; + private static final Long ARTIFACT_SIZE = 6666L; private static final String TENANT = "DEFAULT"; - private static String CONTROLLLER_ID = "123"; + private static final Long TENANT_ID = 123L; + private static final String CONTROLLER_ID = "123"; + private static final Long TARGET_ID = 123L; private AmqpMessageHandlerService amqpMessageHandlerService; + private AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandlerService; + private MessageConverter messageConverter; - private TenantConfigurationManagement tenantConfigurationManagement; + private AmqpControllerAuthentication authenticationManager; + @Mock + private TenantConfigurationManagement tenantConfigurationManagementMock; + + @Mock + private SystemManagement systemManagement; + + @Mock + private Cache cacheMock; + + @Mock + private HostnameResolver hostnameResolverMock; + + @Mock + private ArtifactManagement artifactManagementMock; + + @Mock + private ControllerManagement controllerManagementMock; + + @Mock + private Target targteMock; + private static final TenantConfigurationValue CONFIG_VALUE_FALSE = TenantConfigurationValue . builder().value(Boolean.FALSE).build(); @@ -74,8 +120,6 @@ public class AmqpControllerAuthenticationTest { messageConverter = new Jackson2JsonMessageConverter(); final RabbitTemplate rabbitTemplate = mock(RabbitTemplate.class); when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); - amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, - mock(AmqpMessageDispatcherService.class)); final DdiSecurityProperties secruityProperties = mock(DdiSecurityProperties.class); final Rp rp = mock(Rp.class); @@ -88,30 +132,57 @@ public class AmqpControllerAuthenticationTest { when(ddiAuthentication.getAnonymous()).thenReturn(anonymous); when(anonymous.isEnabled()).thenReturn(false); - tenantConfigurationManagement = mock(TenantConfigurationManagement.class); - - when(tenantConfigurationManagement.getConfigurationValue(any(), eq(Boolean.class))) + when(tenantConfigurationManagementMock.getConfigurationValue(any(), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_FALSE); final ControllerManagement controllerManagement = mock(ControllerManagement.class); - when(controllerManagement.getSecurityTokenByControllerId(anyString())).thenReturn(CONTROLLLER_ID); - amqpMessageHandlerService.setArtifactManagement(mock(ArtifactManagement.class)); + when(controllerManagement.findByControllerId(anyString())).thenReturn(targteMock); + when(controllerManagement.findByTargetId(any(Long.class))).thenReturn(targteMock); + + when(targteMock.getSecurityToken()).thenReturn(CONTROLLER_ID); + when(targteMock.getControllerId()).thenReturn(CONTROLLER_ID); final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); - authenticationManager = new AmqpControllerAuthentication(controllerManagement, tenantConfigurationManagement, - tenantAware, secruityProperties, systemSecurityContext); + final TenantMetaData tenantMetaData = mock(TenantMetaData.class); + when(tenantMetaData.getTenant()).thenReturn(TENANT); + when(systemManagement.getTenantMetadata(TENANT_ID)).thenReturn(tenantMetaData); + + authenticationManager = new AmqpControllerAuthentication(systemManagement, controllerManagement, + tenantConfigurationManagementMock, tenantAware, secruityProperties, systemSecurityContext); authenticationManager.postConstruct(); - amqpMessageHandlerService.setAuthenticationManager(authenticationManager); + + final LocalArtifact testArtifact = new JpaLocalArtifact("afilename", "afilename", new JpaSoftwareModule( + new JpaSoftwareModuleType("a key", "a name", null, 1), "a name", null, null, null)); + + when(artifactManagementMock.findLocalArtifact(ARTIFACT_ID)).thenReturn(testArtifact); + when(artifactManagementMock.findFirstLocalArtifactsBySHA1(SHA1)).thenReturn(testArtifact); + + final DbArtifact artifact = new DbArtifact(); + artifact.setSize(ARTIFACT_SIZE); + artifact.setHashes(new DbArtifactHash("sha1 test", "md5 test")); + when(artifactManagementMock.loadLocalArtifactBinary(testArtifact)).thenReturn(artifact); + + amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, + mock(AmqpMessageDispatcherService.class), controllerManagementMock, new JpaEntityFactory()); + + amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate, + authenticationManager, artifactManagementMock, cacheMock, hostnameResolverMock, + controllerManagementMock); + + when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost")); + + when(controllerManagementMock.hasTargetArtifactAssigned(TARGET_ID, testArtifact)).thenReturn(true); + when(controllerManagementMock.hasTargetArtifactAssigned(CONTROLLER_ID, testArtifact)).thenReturn(true); } @Test @Description("Tests authentication manager without principal") public void testAuthenticationeBadCredantialsWithoutPricipal() { - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); try { authenticationManager.doAuthenticate(securityToken); fail("BadCredentialsException was excepeted since principal was missing"); @@ -124,12 +195,12 @@ public class AmqpControllerAuthenticationTest { @Test @Description("Tests authentication manager without wrong credential") public void testAuthenticationBadCredantialsWithWrongCredential() { - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); - when(tenantConfigurationManagement.getConfigurationValue( + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_TRUE); - securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLER_ID); try { authenticationManager.doAuthenticate(securityToken); fail("BadCredentialsException was excepeted due to wrong credential"); @@ -142,12 +213,12 @@ public class AmqpControllerAuthenticationTest { @Test @Description("Tests authentication successfull") public void testSuccessfullAuthentication() { - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); - when(tenantConfigurationManagement.getConfigurationValue( + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_TRUE); - securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); final Authentication authentication = authenticationManager.doAuthenticate(securityToken); assertThat(authentication).isNotNull(); } @@ -157,13 +228,13 @@ public class AmqpControllerAuthenticationTest { public void testAuthenticationMessageBadCredantialsWithoutPricipal() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -175,17 +246,17 @@ public class AmqpControllerAuthenticationTest { @Description("Tests authentication message without wrong credential") public void testAuthenticationMessageBadCredantialsWithWrongCredential() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); - when(tenantConfigurationManagement.getConfigurationValue( + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_TRUE); - securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLLER_ID); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken 12" + CONTROLLER_ID); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -195,24 +266,81 @@ public class AmqpControllerAuthenticationTest { @Test @Description("Tests authentication message successfull") - public void testSuccessfullMessageAuthentication() { + public void successfullMessageAuthentication() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID, - FileResource.createFileResourceBySha1("12345")); - when(tenantConfigurationManagement.getConfigurationValue( + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, null, CONTROLLER_ID, null, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) .thenReturn(CONFIG_VALUE_TRUE); - securityToken.getHeaders().put(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLLER_ID); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); assertThat(downloadResponse).isNotNull(); - assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.NOT_FOUND.value()); + assertThat(downloadResponse.getDownloadUrl()).isNotNull(); + assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(SecurityContextHolder.getContext()).isNotNull(); + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); + assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName()) + .isEqualTo(PreAuthenticatedAuthenticationToken.class.getName()); + + } + + @Test + @Description("Tests authentication message successfull with targetId intead of controllerId provided and artifactId instead of SHA1.") + public void successfullMessageAuthenticationWithTargetIdAndArtifactId() { + final MessageProperties messageProperties = createMessageProperties(null); + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, null, null, TARGET_ID, + FileResource.createFileResourceByArtifactId(ARTIFACT_ID)); + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) + .thenReturn(CONFIG_VALUE_TRUE); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); + final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, + messageProperties); + + // test + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); + + // verify + final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); + assertThat(downloadResponse).isNotNull(); + assertThat(downloadResponse.getDownloadUrl()).isNotNull(); + assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(SecurityContextHolder.getContext()).isNotNull(); + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); + assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName()) + .isEqualTo(PreAuthenticatedAuthenticationToken.class.getName()); + + } + + @Test + @Description("Tests authentication message successfull") + public void successfullMessageAuthenticationWithTenantid() { + final MessageProperties messageProperties = createMessageProperties(null); + final TenantSecurityToken securityToken = new TenantSecurityToken(null, TENANT_ID, CONTROLLER_ID, TARGET_ID, + FileResource.createFileResourceBySha1(SHA1)); + when(tenantConfigurationManagementMock.getConfigurationValue( + eq(TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED), eq(Boolean.class))) + .thenReturn(CONFIG_VALUE_TRUE); + securityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, "TargetToken " + CONTROLLER_ID); + final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, + messageProperties); + + // test + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); + + // verify + final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); + assertThat(downloadResponse).isNotNull(); + assertThat(downloadResponse.getDownloadUrl()).isNotNull(); + assertThat(downloadResponse.getResponseCode()).isEqualTo(HttpStatus.OK.value()); assertThat(SecurityContextHolder.getContext()).isNotNull(); assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); assertThat(SecurityContextHolder.getContext().getAuthentication().getClass().getName()) diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java index 62bd8e9b7..2fe2709e7 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java @@ -13,9 +13,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; @@ -24,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import org.eclipse.hawkbit.api.ArtifactUrl; import org.eclipse.hawkbit.api.ArtifactUrlHandler; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; @@ -31,11 +30,14 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest; import org.eclipse.hawkbit.eventbus.event.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TenantMetaData; import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; import org.eclipse.hawkbit.util.IpUtil; import org.junit.Test; @@ -49,6 +51,8 @@ import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.test.context.ActiveProfiles; +import com.google.common.collect.Lists; + import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Stories; @@ -60,6 +64,7 @@ import ru.yandex.qatools.allure.annotations.Stories; public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { private static final String TENANT = "default"; + private static final Long TENANT_ID = 4711L; private static final URI AMQP_URI = IpUtil.createAmqpUri("vHost", "mytest"); @@ -71,31 +76,47 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { private DefaultAmqpSenderService senderService; + private SystemManagement systemManagement; + private static final String CONTROLLER_ID = "1"; + private Target testTarget; + @Override public void before() throws Exception { super.before(); + testTarget = entityFactory.generateTarget(CONTROLLER_ID, TEST_TOKEN); + testTarget.getTargetInfo().setAddress(AMQP_URI.toString()); + this.rabbitTemplate = Mockito.mock(RabbitTemplate.class); when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter()); senderService = Mockito.mock(DefaultAmqpSenderService.class); final ArtifactUrlHandler artifactUrlHandlerMock = Mockito.mock(ArtifactUrlHandler.class); - when(artifactUrlHandlerMock.getUrl(anyString(), anyLong(), anyString(), anyString(), anyObject())) - .thenReturn("http://mockurl"); + when(artifactUrlHandlerMock.getUrls(anyObject(), anyObject())) + .thenReturn(Lists.newArrayList(new ArtifactUrl("http", "download", "http://mockurl"))); + + systemManagement = Mockito.mock(SystemManagement.class); + final TenantMetaData tenantMetaData = Mockito.mock(TenantMetaData.class); + when(tenantMetaData.getId()).thenReturn(TENANT_ID); + when(tenantMetaData.getTenant()).thenReturn(TENANT); + + when(systemManagement.getTenantMetadata()).thenReturn(tenantMetaData); amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService, - artifactUrlHandlerMock); + artifactUrlHandlerMock, systemSecurityContext, systemManagement); + } @Test @Description("Verfies that download and install event with no software modul works") public void testSendDownloadRequesWithEmptySoftwareModules() { final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent( - 1L, TENANT, CONTROLLER_ID, 1L, new ArrayList(), AMQP_URI, TEST_TOKEN); + 1L, TENANT, testTarget, 1L, new ArrayList()); amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent); - final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress()); + final Message sendMessage = createArgumentCapture( + targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress()); final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage); assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN); assertTrue("No softwaremmodule should be contained in the request", @@ -107,9 +128,10 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { public void testSendDownloadRequesWithSoftwareModulesAndNoArtifacts() { final DistributionSet dsA = testdataFactory.createDistributionSet(""); final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent( - 1L, TENANT, CONTROLLER_ID, 1L, dsA.getModules(), AMQP_URI, TEST_TOKEN); + 1L, TENANT, testTarget, 1L, dsA.getModules()); amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent); - final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress()); + final Message sendMessage = createArgumentCapture( + targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress()); final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage); assertEquals("Expecting a size of 3 software modules in the reuqest", 3, downloadAndUpdateRequest.getSoftwareModules().size()); @@ -146,9 +168,10 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { Mockito.when(rabbitTemplate.convertSendAndReceive(any())).thenReturn(receivedList); final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent( - 1L, TENANT, CONTROLLER_ID, 1L, dsA.getModules(), AMQP_URI, TEST_TOKEN); + 1L, TENANT, testTarget, 1L, dsA.getModules()); amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent); - final Message sendMessage = createArgumentCapture(targetAssignDistributionSetEvent.getTargetAdress()); + final Message sendMessage = createArgumentCapture( + targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress()); final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage); assertEquals("DownloadAndUpdateRequest event should contains 3 software modules", 3, downloadAndUpdateRequest.getSoftwareModules().size()); diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java index a1591c07c..e4c309412 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java @@ -54,7 +54,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.security.SecurityTokenGenerator; -import org.eclipse.hawkbit.security.SystemSecurityContext; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -81,8 +80,12 @@ import ru.yandex.qatools.allure.annotations.Stories; public class AmqpMessageHandlerServiceTest { private static final String TENANT = "DEFAULT"; + private static final Long TENANT_ID = 123L; + private static String CONTROLLLER_ID = "123"; + private static final Long TARGET_ID = 123L; private AmqpMessageHandlerService amqpMessageHandlerService; + private AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandlerService; private MessageConverter messageConverter; @@ -113,22 +116,16 @@ public class AmqpMessageHandlerServiceTest { @Mock private RabbitTemplate rabbitTemplate; - @Mock - private SystemSecurityContext systemSecurityContextMock; - @Before public void before() throws Exception { messageConverter = new Jackson2JsonMessageConverter(); when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); - amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock); - amqpMessageHandlerService.setControllerManagement(controllerManagementMock); - amqpMessageHandlerService.setAuthenticationManager(authenticationManagerMock); - amqpMessageHandlerService.setArtifactManagement(artifactManagementMock); - amqpMessageHandlerService.setCache(cacheMock); - amqpMessageHandlerService.setHostnameResolver(hostnameResolverMock); - amqpMessageHandlerService.setEntityFactory(entityFactoryMock); - amqpMessageHandlerService.setSystemSecurityContext(systemSecurityContextMock); + amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock, + controllerManagementMock, entityFactoryMock); + amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate, + authenticationManagerMock, artifactManagementMock, cacheMock, hostnameResolverMock, + controllerManagementMock); } @Test @@ -279,13 +276,13 @@ public class AmqpMessageHandlerServiceTest { @Description("Tests that an download request is denied for an artifact which does not exists") public void authenticationRequestDeniedForArtifactWhichDoesNotExists() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLLER_ID, TARGET_ID, FileResource.createFileResourceBySha1("12345")); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -298,7 +295,7 @@ public class AmqpMessageHandlerServiceTest { @Description("Tests that an download request is denied for an artifact which is not assigned to the requested target") public void authenticationRequestDeniedForArtifactWhichIsNotAssignedToTarget() { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLLER_ID, TARGET_ID, FileResource.createFileResourceBySha1("12345")); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); @@ -309,7 +306,7 @@ public class AmqpMessageHandlerServiceTest { .thenThrow(EntityNotFoundException.class); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -322,7 +319,7 @@ public class AmqpMessageHandlerServiceTest { @Description("Tests that an download request is allowed for an artifact which exists and assigned to the requested target") public void authenticationRequestAllowedForArtifactWhichExistsAndAssignedToTarget() throws MalformedURLException { final MessageProperties messageProperties = createMessageProperties(null); - final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", + final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, TENANT_ID, CONTROLLLER_ID, TARGET_ID, FileResource.createFileResourceBySha1("12345")); final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken, messageProperties); @@ -340,7 +337,7 @@ public class AmqpMessageHandlerServiceTest { when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost")); // test - final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message); + final Message onMessage = amqpAuthenticationMessageHandlerService.onAuthenticationRequest(message); // verify final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage); @@ -364,15 +361,12 @@ public class AmqpMessageHandlerServiceTest { when(controllerManagementMock.addUpdateActionStatus(Matchers.any())).thenReturn(action); when(entityFactoryMock.generateActionStatus()).thenReturn(new JpaActionStatus()); // for the test the same action can be used - when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any())) - .thenReturn(Optional.of(action)); + when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any())).thenReturn(Optional.of(action)); final List softwareModuleList = createSoftwareModuleList(); when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any())) .thenReturn(softwareModuleList); - when(systemSecurityContextMock.runAsSystem(anyObject())).thenReturn("securityToken"); - final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT); messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); final ActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(ActionStatus.FINISHED, 23L); @@ -393,10 +387,10 @@ public class AmqpMessageHandlerServiceTest { final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = captorTargetAssignDistributionSetEvent .getValue(); - assertThat(targetAssignDistributionSetEvent.getControllerId()).as("event has wrong controller id") + assertThat(targetAssignDistributionSetEvent.getTarget().getControllerId()).as("event has wrong controller id") .isEqualTo("target1"); - assertThat(targetAssignDistributionSetEvent.getTargetToken()).as("targetoken not filled correctly") - .isEqualTo(action.getTarget().getSecurityToken()); + assertThat(targetAssignDistributionSetEvent.getTarget().getSecurityToken()) + .as("targetoken not filled correctly").isEqualTo(action.getTarget().getSecurityToken()); assertThat(targetAssignDistributionSetEvent.getActionId()).as("event has wrong action id").isEqualTo(22L); assertThat(targetAssignDistributionSetEvent.getSoftwareModules()).as("event has wrong sofware modules") .isEqualTo(softwareModuleList); diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java index 20d03bdd6..582bcf130 100644 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java +++ b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java @@ -52,8 +52,8 @@ public class BaseAmqpServiceTest { final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); actionUpdateStatus.setActionId(1L); actionUpdateStatus.setSoftwareModuleId(2L); - actionUpdateStatus.getMessage().add("Message 1"); - actionUpdateStatus.getMessage().add("Message 2"); + actionUpdateStatus.addMessage("Message 1"); + actionUpdateStatus.addMessage("Message 2"); final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus, new MessageProperties()); diff --git a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java b/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java deleted file mode 100644 index ea01bc0ec..000000000 --- a/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/util/PropertyBasedArtifactUrlHandlerTest.java +++ /dev/null @@ -1,95 +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.util; - -import static org.junit.Assert.assertEquals; - -import org.eclipse.hawkbit.AmqpTestConfiguration; -import org.eclipse.hawkbit.api.ArtifactUrlHandler; -import org.eclipse.hawkbit.api.UrlProtocol; -import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.LocalArtifact; -import org.eclipse.hawkbit.repository.model.SoftwareModule; -import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.SpringApplicationConfiguration; - -import ru.yandex.qatools.allure.annotations.Description; -import ru.yandex.qatools.allure.annotations.Features; -import ru.yandex.qatools.allure.annotations.Stories; - -/** - * Tests for creating urls to download artifacts. - */ -@Features("Component Tests - Artifact URL Handler") -@Stories("Test to generate the artifact download URL") -@SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class, - org.eclipse.hawkbit.RepositoryApplicationConfiguration.class }) -public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTest { - - private static final String HTTPS_LOCALHOST = "https://localhost/"; - private static final String HTTP_LOCALHOST = "http://localhost/"; - - @Autowired - private ArtifactUrlHandler urlHandlerProperties; - - private LocalArtifact localArtifact; - private static final String CONTROLLER_ID = "Test"; - private String fileName; - private Long softwareModuleId; - private String sha1Hash; - - @Before - public void setup() { - final DistributionSet dsA = testdataFactory.createDistributionSet(""); - final SoftwareModule module = dsA.getModules().iterator().next(); - localArtifact = testdataFactory.createLocalArtifacts(module.getId()).stream().findAny().get(); - softwareModuleId = localArtifact.getSoftwareModule().getId(); - fileName = localArtifact.getFilename(); - sha1Hash = localArtifact.getSha1Hash(); - - } - - @Test - @Description("Tests the generation of http download url.") - public void testHttpUrl() { - - final String url = urlHandlerProperties.getUrl(CONTROLLER_ID, softwareModuleId, fileName, sha1Hash, - UrlProtocol.HTTP); - assertEquals("http is build incorrect", - HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + CONTROLLER_ID - + "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/" - + localArtifact.getFilename(), - url); - } - - @Test - @Description("Tests the generation of https download url.") - public void testHttpsUrl() { - final String url = urlHandlerProperties.getUrl(CONTROLLER_ID, softwareModuleId, fileName, sha1Hash, - UrlProtocol.HTTPS); - assertEquals("https is build incorrect", - HTTPS_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" + CONTROLLER_ID - + "/softwaremodules/" + localArtifact.getSoftwareModule().getId() + "/artifacts/" - + localArtifact.getFilename(), - url); - } - - @Test - @Description("Tests the generation of coap download url.") - public void testCoapUrl() { - final String url = urlHandlerProperties.getUrl(CONTROLLER_ID, softwareModuleId, fileName, sha1Hash, - UrlProtocol.COAP); - - assertEquals("coap is build incorrect", "coap://127.0.0.1:5683/fw/" + tenantAware.getCurrentTenant() + "/" - + CONTROLLER_ID + "/sha1/" + localArtifact.getSha1Hash(), url); - } -} diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java index ac0926080..2262cab96 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/ActionUpdateStatus.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.dmf.json.model; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -18,9 +19,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** * JSON representation of action update status. - * - * - * */ @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) @@ -32,7 +30,7 @@ public class ActionUpdateStatus { @JsonProperty(required = true) private ActionStatus actionStatus; @JsonProperty - private final List message = new ArrayList<>(); + private List message; public Long getActionId() { return actionId; @@ -59,7 +57,19 @@ public class ActionUpdateStatus { } public List getMessage() { - return message; + if (message == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(message); + } + + public boolean addMessage(final String message) { + if (this.message == null) { + this.message = new ArrayList<>(); + } + + return this.message.add(message); } } diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/Artifact.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/Artifact.java index 27da75fb0..8375663b8 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/Artifact.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/Artifact.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.dmf.json.model; -import java.util.EnumMap; +import java.util.Collections; import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -25,15 +25,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class Artifact { - - /** - * Represented the supported protocols for artifact url's. - * - */ - public enum UrlProtocol { - COAP, HTTP, HTTPS - } - @JsonProperty private String filename; @@ -44,13 +35,17 @@ public class Artifact { private Long size; @JsonProperty - private Map urls = new EnumMap<>(UrlProtocol.class); + private Map urls; - public Map getUrls() { - return urls; + public Map getUrls() { + if (urls == null) { + return Collections.emptyMap(); + } + + return Collections.unmodifiableMap(urls); } - public void setUrls(final Map urls) { + public void setUrls(final Map urls) { this.urls = urls; } diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DownloadAndUpdateRequest.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DownloadAndUpdateRequest.java index 88cb80975..8664f639e 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DownloadAndUpdateRequest.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DownloadAndUpdateRequest.java @@ -8,7 +8,8 @@ */ package org.eclipse.hawkbit.dmf.json.model; -import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -30,7 +31,7 @@ public class DownloadAndUpdateRequest { private String targetSecurityToken; @JsonProperty - private final List softwareModules = new LinkedList<>(); + private List softwareModules; public Long getActionId() { return actionId; @@ -49,7 +50,11 @@ public class DownloadAndUpdateRequest { } public List getSoftwareModules() { - return softwareModules; + if (softwareModules == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(softwareModules); } /** @@ -59,6 +64,10 @@ public class DownloadAndUpdateRequest { * the module */ public void addSoftwareModule(final SoftwareModule createSoftwareModule) { + if (softwareModules == null) { + softwareModules = new ArrayList<>(); + } + softwareModules.add(createSoftwareModule); } diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/SoftwareModule.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/SoftwareModule.java index 193f33575..70a7880d8 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/SoftwareModule.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/SoftwareModule.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.dmf.json.model; -import java.util.LinkedList; +import java.util.Collections; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -35,7 +35,7 @@ public class SoftwareModule { @JsonProperty private String moduleVersion; @JsonProperty - private List artifacts = new LinkedList<>(); + private List artifacts; public String getModuleType() { return moduleType; @@ -54,7 +54,11 @@ public class SoftwareModule { } public List getArtifacts() { - return artifacts; + if (artifacts == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(artifacts); } public Long getModuleId() { diff --git a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/TenantSecurityToken.java b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/TenantSecurityToken.java index 19293f3eb..d2248fed8 100644 --- a/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/TenantSecurityToken.java +++ b/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/TenantSecurityToken.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.dmf.json.model; +import java.util.Collections; import java.util.Map; import java.util.TreeMap; @@ -27,16 +28,47 @@ public class TenantSecurityToken { public static final String AUTHORIZATION_HEADER = "Authorization"; - @JsonProperty - private final String tenant; - @JsonProperty + @JsonProperty(required = false) + private String tenant; + @JsonProperty(required = false) + private final Long tenantId; + @JsonProperty(required = false) private final String controllerId; @JsonProperty(required = false) - private Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + private final Long targetId; + + @JsonProperty(required = false) + private Map headers; @JsonProperty(required = false) private final FileResource fileResource; + /** + * Constructor. + * + * @param tenant + * the tenant for the security token + * @param tenantId + * alternative tenant identification by technical ID + * @param controllerId + * the ID of the controller for the security token + * @param targetId + * alternative target identification by technical ID + * @param fileResource + * the file to obtain + */ + @JsonCreator + public TenantSecurityToken(@JsonProperty("tenant") final String tenant, + @JsonProperty("tenantId") final Long tenantId, @JsonProperty("controllerId") final String controllerId, + @JsonProperty("targetId") final Long targetId, + @JsonProperty("fileResource") final FileResource fileResource) { + this.tenant = tenant; + this.tenantId = tenantId; + this.controllerId = controllerId; + this.targetId = targetId; + this.fileResource = fileResource; + } + /** * Constructor. * @@ -47,13 +79,26 @@ public class TenantSecurityToken { * @param fileResource * the file to obtain */ - @JsonCreator - public TenantSecurityToken(@JsonProperty("tenant") final String tenant, - @JsonProperty("controllerId") final String controllerId, - @JsonProperty("fileResource") final FileResource fileResource) { + public TenantSecurityToken(final String tenant, final String controllerId, final FileResource fileResource) { + this(tenant, null, controllerId, null, fileResource); + } + + /** + * Constructor. + * + * @param tenantId + * the tenant for the security token + * @param targetId + * target identification by technical ID + * @param fileResource + * the file to obtain + */ + public TenantSecurityToken(final Long tenantId, final Long targetId, final FileResource fileResource) { + this(null, tenantId, null, targetId, fileResource); + } + + public void setTenant(final String tenant) { this.tenant = tenant; - this.controllerId = controllerId; - this.fileResource = fileResource; } public String getTenant() { @@ -65,13 +110,25 @@ public class TenantSecurityToken { } public Map getHeaders() { - return headers; + if (headers == null) { + return Collections.emptyMap(); + } + + return Collections.unmodifiableMap(headers); } public FileResource getFileResource() { return fileResource; } + public Long getTenantId() { + return tenantId; + } + + public Long getTargetId() { + return targetId; + } + /** * Gets a header value. * @@ -80,6 +137,10 @@ public class TenantSecurityToken { * @return the value */ public String getHeader(final String name) { + if (headers == null) { + return null; + } + return headers.get(name); } @@ -88,6 +149,24 @@ public class TenantSecurityToken { this.headers.putAll(headers); } + /** + * Associates the specified header value with the specified name. + * + * @param name + * of the header + * @param value + * of the header + * + * @return the previous value associated with the name, or + * null if there was no mapping for name. + */ + public String putHeader(final String name, final String value) { + if (headers == null) { + headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + return headers.put(name, value); + } + /** * File resource descriptor which is used to ask for the resource to * download e.g. The lookup of the file can be different e.g. by SHA1 hash @@ -99,6 +178,8 @@ public class TenantSecurityToken { @JsonProperty(required = false) private String sha1; @JsonProperty(required = false) + private Long artifactId; + @JsonProperty(required = false) private String filename; @JsonProperty(required = false) private SoftwareModuleFilenameResource softwareModuleFilenameResource; @@ -128,6 +209,14 @@ public class TenantSecurityToken { this.softwareModuleFilenameResource = softwareModuleFilenameResource; } + public Long getArtifactId() { + return artifactId; + } + + public void setArtifactId(final Long artifactId) { + this.artifactId = artifactId; + } + /** * factory method to create a file resource for an SHA1 lookup. * @@ -141,6 +230,19 @@ public class TenantSecurityToken { return resource; } + /** + * factory method to create a file resource for an artifact ID lookup. + * + * @param artifactId + * the artifact IF key of the file to obtain + * @return the {@link FileResource} with SHA1 key set + */ + public static FileResource createFileResourceByArtifactId(final Long artifactId) { + final FileResource resource = new FileResource(); + resource.artifactId = artifactId; + return resource; + } + /** * factory method to create a file resource for an filename lookup. * @@ -173,7 +275,7 @@ public class TenantSecurityToken { @Override public String toString() { - return "FileResource [sha1=" + sha1 + ", filename=" + filename + "]"; + return "FileResource [sha1=" + sha1 + ", artifactId=" + artifactId + ", filename=" + filename + "]"; } /** diff --git a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java index 2f2726d98..f62307789 100644 --- a/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java +++ b/hawkbit-http-security/src/main/java/org/eclipse/hawkbit/security/AbstractHttpControllerAuthenticationFilter.java @@ -168,10 +168,10 @@ public abstract class AbstractHttpControllerAuthenticationFilter extends Abstrac private TenantSecurityToken createTenantSecruityTokenVariables(final HttpServletRequest request, final String tenant, final String controllerId) { - final TenantSecurityToken secruityToken = new TenantSecurityToken(tenant, controllerId, + final TenantSecurityToken secruityToken = new TenantSecurityToken(tenant, null, controllerId, null, FileResource.createFileResourceBySha1("")); final UnmodifiableIterator forEnumeration = Iterators.forEnumeration(request.getHeaderNames()); - forEnumeration.forEachRemaining(header -> secruityToken.getHeaders().put(header, request.getHeader(header))); + forEnumeration.forEachRemaining(header -> secruityToken.putHeader(header, request.getHeader(header))); return secruityToken; } diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/PagedList.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/PagedList.java index 173f3ce31..534cfa98d 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/PagedList.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/PagedList.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.mgmt.json.model; +import java.util.Collections; import java.util.List; import javax.validation.constraints.NotNull; @@ -72,7 +73,7 @@ public class PagedList extends ResourceSupport { } public List getContent() { - return content; + return Collections.unmodifiableList(content); } } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetMapper.java index 4a0f8d90e..acd088c85 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetMapper.java @@ -12,7 +12,10 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet; @@ -34,10 +37,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; /** * A mapper which maps repository model to RESTful model representation and * back. - * - * - * - * */ public final class MgmtDistributionSetMapper { private MgmtDistributionSetMapper() { @@ -75,15 +74,13 @@ public final class MgmtDistributionSetMapper { * to use for conversion * @return converted list of {@link DistributionSet}s */ - static List dsFromRequest(final Iterable sets, + static List dsFromRequest(final Collection sets, final SoftwareManagement softwareManagement, final DistributionSetManagement distributionSetManagement, final EntityFactory entityFactory) { - final List mappedList = new ArrayList<>(); - for (final MgmtDistributionSetRequestBodyPost dsRest : sets) { - mappedList.add(fromRequest(dsRest, softwareManagement, distributionSetManagement, entityFactory)); - } - return mappedList; + return sets.stream() + .map(dsRest -> fromRequest(dsRest, softwareManagement, distributionSetManagement, entityFactory)) + .collect(Collectors.toList()); } @@ -139,15 +136,12 @@ public final class MgmtDistributionSetMapper { */ static List fromRequestDsMetadata(final DistributionSet ds, final List metadata, final EntityFactory entityFactory) { - final List mappedList = new ArrayList<>(metadata.size()); - for (final MgmtMetadata metadataRest : metadata) { - if (metadataRest.getKey() == null) { - throw new IllegalArgumentException("the key of the metadata must be present"); - } - mappedList.add( - entityFactory.generateDistributionSetMetadata(ds, metadataRest.getKey(), metadataRest.getValue())); + if (metadata == null) { + return Collections.emptyList(); } - return mappedList; + + return metadata.stream().map(metadataRest -> entityFactory.generateDistributionSetMetadata(ds, + metadataRest.getKey(), metadataRest.getValue())).collect(Collectors.toList()); } /** @@ -196,15 +190,12 @@ public final class MgmtDistributionSetMapper { return result; } - static List toResponseDistributionSets(final Iterable sets) { - final List response = new ArrayList<>(); - if (sets != null) { - - for (final DistributionSet set : sets) { - response.add(toResponse(set)); - } + static List toResponseDistributionSets(final Collection sets) { + if (sets == null) { + return Collections.emptyList(); } - return response; + + return sets.stream().map(MgmtDistributionSetMapper::toResponse).collect(Collectors.toList()); } static MgmtMetadata toResponseDsMetadata(final DistributionSetMetadata metadata) { @@ -224,14 +215,10 @@ public final class MgmtDistributionSetMapper { } static List toResponseFromDsList(final List sets) { - final List mappedList = new ArrayList<>(); - if (sets != null) { - for (final DistributionSet set : sets) { - final MgmtDistributionSet response = toResponse(set); - - mappedList.add(response); - } + if (sets == null) { + return Collections.emptyList(); } - return mappedList; + + return sets.stream().map(MgmtDistributionSetMapper::toResponse).collect(Collectors.toList()); } } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java index 721995d88..b49654460 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetResource.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.mgmt.rest.resource; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -123,7 +124,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi { .runAsSystem(() -> this.systemManagement.getTenantMetadata().getDefaultDsType().getKey()); sets.stream().filter(ds -> ds.getType() == null).forEach(ds -> ds.setType(defaultDsKey)); - final Iterable createdDSets = this.distributionSetManagement + final Collection createdDSets = this.distributionSetManagement .createDistributionSets(MgmtDistributionSetMapper.dsFromRequest(sets, this.softwareManagement, this.distributionSetManagement, entityFactory)); diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeMapper.java index 675fe9df0..33b81ab73 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeMapper.java @@ -11,8 +11,10 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetType; import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeRequestBodyPost; @@ -39,13 +41,13 @@ final class MgmtDistributionSetTypeMapper { static List smFromRequest(final EntityFactory entityFactory, final SoftwareManagement softwareManagement, - final Iterable smTypesRest) { - final List mappedList = new ArrayList<>(); - - for (final MgmtDistributionSetTypeRequestBodyPost smRest : smTypesRest) { - mappedList.add(fromRequest(entityFactory, softwareManagement, smRest)); + final Collection smTypesRest) { + if (smTypesRest == null) { + return Collections.emptyList(); } - return mappedList; + + return smTypesRest.stream().map(smRest -> fromRequest(entityFactory, softwareManagement, smRest)) + .collect(Collectors.toList()); } static DistributionSetType fromRequest(final EntityFactory entityFactory, @@ -91,19 +93,19 @@ final class MgmtDistributionSetTypeMapper { } static List toTypesResponse(final List types) { - final List response = new ArrayList<>(); - for (final DistributionSetType dsType : types) { - response.add(toResponse(dsType)); + if (types == null) { + return Collections.emptyList(); } - return response; + + return types.stream().map(MgmtDistributionSetTypeMapper::toResponse).collect(Collectors.toList()); } static List toListResponse(final List types) { - final List response = new ArrayList<>(); - for (final DistributionSetType dsType : types) { - response.add(toResponse(dsType)); + if (types == null) { + return Collections.emptyList(); } - return response; + + return types.stream().map(MgmtDistributionSetTypeMapper::toResponse).collect(Collectors.toList()); } static MgmtDistributionSetType toResponse(final DistributionSetType type) { diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java index d69e4d8d4..fe62ff369 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResource.java @@ -8,6 +8,12 @@ */ package org.eclipse.hawkbit.mgmt.rest.resource; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtDistributionSetTypeMapper.toResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtDistributionSetTypeMapper.toTypesResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleTypeMapper.toResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleTypeMapper.toTypesResponse; +import static org.springframework.http.HttpStatus.CREATED; + import java.util.List; import org.eclipse.hawkbit.mgmt.json.model.MgmtId; @@ -32,7 +38,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -42,8 +47,6 @@ import org.springframework.web.bind.annotation.RestController; /** * REST Resource handling for {@link SoftwareModule} and related * {@link Artifact} CRUD operations. - * - * */ @RestController public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeRestApi { @@ -67,7 +70,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); final Sort sorting = PagingUtility.sanitizeDistributionSetTypeSortParam(sortParam); - final Pageable pageable = new OffsetBasedPageRequest(sanitizedOffsetParam, sanitizedLimitParam, sorting); final Slice findModuleTypessAll; @@ -82,31 +84,32 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR final List rest = MgmtDistributionSetTypeMapper .toListResponse(findModuleTypessAll.getContent()); - return new ResponseEntity<>(new PagedList<>(rest, countModulesAll), HttpStatus.OK); + return ResponseEntity.ok(new PagedList<>(rest, countModulesAll)); } @Override public ResponseEntity getDistributionSetType( @PathVariable("distributionSetTypeId") final Long distributionSetTypeId) { - final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - return new ResponseEntity<>(MgmtDistributionSetTypeMapper.toResponse(foundType), HttpStatus.OK); + final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); + return ResponseEntity.ok(toResponse(foundType)); } @Override public ResponseEntity deleteDistributionSetType( @PathVariable("distributionSetTypeId") final Long distributionSetTypeId) { - final DistributionSetType module = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); + final DistributionSetType module = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); distributionSetManagement.deleteDistributionSetType(module); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override public ResponseEntity updateDistributionSetType( @PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @RequestBody final MgmtDistributionSetTypeRequestBodyPut restDistributionSetType) { + final DistributionSetType type = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); // only description can be modified @@ -117,8 +120,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR final DistributionSetType updatedDistributionSetType = distributionSetManagement .updateDistributionSetType(type); - return new ResponseEntity<>(MgmtDistributionSetTypeMapper.toResponse(updatedDistributionSetType), - HttpStatus.OK); + return ResponseEntity.ok(toResponse(updatedDistributionSetType)); } @Override @@ -128,16 +130,17 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR final List createdSoftwareModules = distributionSetManagement.createDistributionSetTypes( MgmtDistributionSetTypeMapper.smFromRequest(entityFactory, softwareManagement, distributionSetTypes)); - return new ResponseEntity<>(MgmtDistributionSetTypeMapper.toTypesResponse(createdSoftwareModules), - HttpStatus.CREATED); + return ResponseEntity.status(CREATED).body(toTypesResponse(createdSoftwareModules)); } private DistributionSetType findDistributionSetTypeWithExceptionIfNotFound(final Long distributionSetTypeId) { + final DistributionSetType module = distributionSetManagement.findDistributionSetTypeById(distributionSetTypeId); if (module == null) { throw new EntityNotFoundException( "DistributionSetType with Id {" + distributionSetTypeId + "} does not exist"); } + return module; } @@ -146,8 +149,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("distributionSetTypeId") final Long distributionSetTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toListResponse(foundType.getMandatoryModuleTypes()), - HttpStatus.OK); + return ResponseEntity.ok(toTypesResponse(foundType.getMandatoryModuleTypes())); } @Override @@ -156,7 +158,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId); if (!foundType.containsMandatoryModuleType(foundSmType)) { @@ -164,7 +165,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR "Software module with given ID is not part of this distribution set type!"); } - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toResponse(foundSmType), HttpStatus.OK); + return ResponseEntity.ok(toResponse(foundSmType)); } @Override @@ -173,7 +174,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId); if (!foundType.containsOptionalModuleType(foundSmType)) { @@ -181,7 +181,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR "Software module with given ID is not part of this distribution set type!"); } - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toResponse(foundSmType), HttpStatus.OK); + return ResponseEntity.ok(toResponse(foundSmType)); } @Override @@ -189,9 +189,7 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("distributionSetTypeId") final Long distributionSetTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - - return new ResponseEntity<>(MgmtSoftwareModuleTypeMapper.toListResponse(foundType.getOptionalModuleTypes()), - HttpStatus.OK); + return ResponseEntity.ok(toTypesResponse(foundType.getOptionalModuleTypes())); } @Override @@ -200,7 +198,6 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId); if (!foundType.containsMandatoryModuleType(foundSmType)) { @@ -209,18 +206,17 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR } foundType.removeModuleType(softwareModuleTypeId); - distributionSetManagement.updateDistributionSetType(foundType); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override public ResponseEntity removeOptionalModule( @PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @PathVariable("softwareModuleTypeId") final Long softwareModuleTypeId) { - final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); + final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); final SoftwareModuleType foundSmType = findSoftwareModuleTypeWithExceptionIfNotFound(softwareModuleTypeId); if (!foundType.containsOptionalModuleType(foundSmType)) { @@ -229,10 +225,9 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR } foundType.removeModuleType(softwareModuleTypeId); - distributionSetManagement.updateDistributionSetType(foundType); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override @@ -240,14 +235,11 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @RequestBody final MgmtId smtId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType smType = findSoftwareModuleTypeWithExceptionIfNotFound(smtId.getId()); - foundType.addMandatoryModuleType(smType); - distributionSetManagement.updateDistributionSetType(foundType); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override @@ -255,23 +247,22 @@ public class MgmtDistributionSetTypeResource implements MgmtDistributionSetTypeR @PathVariable("distributionSetTypeId") final Long distributionSetTypeId, @RequestBody final MgmtId smtId) { final DistributionSetType foundType = findDistributionSetTypeWithExceptionIfNotFound(distributionSetTypeId); - final SoftwareModuleType smType = findSoftwareModuleTypeWithExceptionIfNotFound(smtId.getId()); - foundType.addOptionalModuleType(smType); distributionSetManagement.updateDistributionSetType(foundType); - return new ResponseEntity<>(HttpStatus.OK); - + return ResponseEntity.ok().build(); } private SoftwareModuleType findSoftwareModuleTypeWithExceptionIfNotFound(final Long softwareModuleTypeId) { + final SoftwareModuleType module = softwareManagement.findSoftwareModuleTypeById(softwareModuleTypeId); if (module == null) { throw new EntityNotFoundException( "SoftwareModuleType with Id {" + softwareModuleTypeId + "} does not exist"); } + return module; } } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java index 7467a45b0..2a092851d 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutMapper.java @@ -11,8 +11,9 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutCondition.Condition; import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutErrorAction.ErrorAction; @@ -48,9 +49,11 @@ final class MgmtRolloutMapper { } static List toResponseRollout(final List rollouts) { - final List result = new ArrayList<>(rollouts.size()); - rollouts.forEach(r -> result.add(toResponseRollout(r))); - return result; + if (rollouts == null) { + return Collections.emptyList(); + } + + return rollouts.stream().map(MgmtRolloutMapper::toResponseRollout).collect(Collectors.toList()); } static MgmtRolloutResponseBody toResponseRollout(final Rollout rollout) { @@ -103,9 +106,11 @@ final class MgmtRolloutMapper { } static List toResponseRolloutGroup(final List rollouts) { - final List result = new ArrayList<>(rollouts.size()); - rollouts.forEach(r -> result.add(toResponseRolloutGroup(r))); - return result; + if (rollouts == null) { + return Collections.emptyList(); + } + + return rollouts.stream().map(MgmtRolloutMapper::toResponseRolloutGroup).collect(Collectors.toList()); } static MgmtRolloutGroupResponseBody toResponseRolloutGroup(final RolloutGroup rolloutGroup) { diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java index 3960fb3c2..2166a7e43 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java @@ -11,8 +11,10 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata; import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact; @@ -65,60 +67,46 @@ public final class MgmtSoftwareModuleMapper { } static List fromRequestSwMetadata(final EntityFactory entityFactory, - final SoftwareModule sw, final List metadata) { - final List mappedList = new ArrayList<>(metadata.size()); - for (final MgmtMetadata metadataRest : metadata) { - if (metadataRest.getKey() == null) { - throw new IllegalArgumentException("the key of the metadata must be present"); - } - mappedList.add( - entityFactory.generateSoftwareModuleMetadata(sw, metadataRest.getKey(), metadataRest.getValue())); + final SoftwareModule sw, final Collection metadata) { + if (metadata == null) { + return Collections.emptyList(); } - return mappedList; + + return metadata.stream().map(metadataRest -> entityFactory.generateSoftwareModuleMetadata(sw, + metadataRest.getKey(), metadataRest.getValue())).collect(Collectors.toList()); } static List smFromRequest(final EntityFactory entityFactory, - final Iterable smsRest, final SoftwareManagement softwareManagement) { - final List mappedList = new ArrayList<>(); - for (final MgmtSoftwareModuleRequestBodyPost smRest : smsRest) { - mappedList.add(fromRequest(entityFactory, smRest, softwareManagement)); + final Collection smsRest, final SoftwareManagement softwareManagement) { + if (smsRest == null) { + return Collections.emptyList(); } - return mappedList; + + return smsRest.stream().map(smRest -> fromRequest(entityFactory, smRest, softwareManagement)) + .collect(Collectors.toList()); } /** * Create response for sw modules. * - * @param baseSoftareModules + * @param softwareModules * the modules * @return the response */ - public static List toResponse(final List baseSoftareModules) { - final List mappedList = new ArrayList<>(); - if (baseSoftareModules != null) { - for (final SoftwareModule target : baseSoftareModules) { - final MgmtSoftwareModule response = toResponse(target); - - mappedList.add(response); - } + public static List toResponse(final Collection softwareModules) { + if (softwareModules == null) { + return Collections.emptyList(); } - return mappedList; + + return softwareModules.stream().map(MgmtSoftwareModuleMapper::toResponse).collect(Collectors.toList()); } - static List toResponseSoftwareModules(final Iterable softwareModules) { - final List response = new ArrayList<>(); - for (final SoftwareModule softwareModule : softwareModules) { - response.add(toResponse(softwareModule)); + static List toResponseSwMetadata(final Collection metadata) { + if (metadata == null) { + return Collections.emptyList(); } - return response; - } - static List toResponseSwMetadata(final List metadata) { - final List mappedList = new ArrayList<>(metadata.size()); - for (final SoftwareModuleMetadata distributionSetMetadata : metadata) { - mappedList.add(toResponseSwMetadata(distributionSetMetadata)); - } - return mappedList; + return metadata.stream().map(MgmtSoftwareModuleMapper::toResponseSwMetadata).collect(Collectors.toList()); } static MgmtMetadata toResponseSwMetadata(final SoftwareModuleMetadata metadata) { @@ -194,15 +182,11 @@ public final class MgmtSoftwareModuleMapper { return artifactRest; } - static List artifactsToResponse(final List artifacts) { - final List mappedList = new ArrayList<>(); - - if (artifacts != null) { - for (final Artifact artifact : artifacts) { - final MgmtArtifact response = toResponse(artifact); - mappedList.add(response); - } + static List artifactsToResponse(final Collection artifacts) { + if (artifacts == null) { + return Collections.emptyList(); } - return mappedList; + + return artifacts.stream().map(MgmtSoftwareModuleMapper::toResponse).collect(Collectors.toList()); } } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java index b475c473f..1706a8030 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResource.java @@ -8,7 +8,15 @@ */ package org.eclipse.hawkbit.mgmt.rest.resource; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleMapper.artifactsToResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleMapper.toResponse; +import static org.eclipse.hawkbit.mgmt.rest.resource.MgmtSoftwareModuleMapper.toResponseSwMetadata; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.CREATED; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; + import java.io.IOException; +import java.util.Collection; import java.util.List; import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata; @@ -46,10 +54,10 @@ import org.springframework.web.multipart.MultipartFile; /** * REST Resource handling for {@link SoftwareModule} and related * {@link Artifact} CRUD operations. - * */ @RestController public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { + private static final Logger LOG = LoggerFactory.getLogger(MgmtSoftwareModuleResource.class); @Autowired @@ -69,7 +77,7 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { @RequestParam(value = "sha1sum", required = false) final String sha1Sum) { if (file.isEmpty()) { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(BAD_REQUEST); } String fileName = optionalFileName; @@ -81,10 +89,10 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { final Artifact result = artifactManagement.createLocalArtifact(file.getInputStream(), softwareModuleId, fileName, md5Sum == null ? null : md5Sum.toLowerCase(), sha1Sum == null ? null : sha1Sum.toLowerCase(), false, file.getContentType()); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(result), HttpStatus.CREATED); + return ResponseEntity.status(CREATED).body(toResponse(result)); } catch (final IOException e) { LOG.error("Failed to store artifact", e); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + return new ResponseEntity<>(INTERNAL_SERVER_ERROR); } } @@ -92,31 +100,34 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { @ResponseBody public ResponseEntity> getArtifacts( @PathVariable("softwareModuleId") final Long softwareModuleId) { - final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.artifactsToResponse(module.getArtifacts()), HttpStatus.OK); + final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); + return ResponseEntity.ok(artifactsToResponse(module.getArtifacts())); } @Override @ResponseBody + // Exception squid:S3655 - Optional access is checked in + // findSoftwareModuleWithExceptionIfNotFound + // subroutine + @SuppressWarnings("squid:S3655") public ResponseEntity getArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("artifactId") final Long artifactId) { + final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(module.getLocalArtifact(artifactId).get()), - HttpStatus.OK); + return ResponseEntity.ok(toResponse(module.getLocalArtifact(artifactId).get())); } @Override @ResponseBody public ResponseEntity deleteArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("artifactId") final Long artifactId) { - findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); + findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); artifactManagement.deleteLocalArtifact(artifactId); - return new ResponseEntity<>(HttpStatus.OK); - + return ResponseEntity.ok().build(); } @Override @@ -143,33 +154,34 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { } final List rest = MgmtSoftwareModuleMapper.toResponse(findModulesAll.getContent()); - return new ResponseEntity<>(new PagedList<>(rest, countModulesAll), HttpStatus.OK); + return ResponseEntity.ok(new PagedList<>(rest, countModulesAll)); } @Override public ResponseEntity getSoftwareModule( @PathVariable("softwareModuleId") final Long softwareModuleId) { - final SoftwareModule findBaseSoftareModule = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(findBaseSoftareModule), HttpStatus.OK); + final SoftwareModule findBaseSoftareModule = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); + return ResponseEntity.ok(toResponse(findBaseSoftareModule)); } @Override public ResponseEntity> createSoftwareModules( @RequestBody final List softwareModules) { + LOG.debug("creating {} softwareModules", softwareModules.size()); - final Iterable createdSoftwareModules = softwareManagement.createSoftwareModule( + final Collection createdSoftwareModules = softwareManagement.createSoftwareModule( MgmtSoftwareModuleMapper.smFromRequest(entityFactory, softwareModules, softwareManagement)); LOG.debug("{} softwareModules created, return status {}", softwareModules.size(), HttpStatus.CREATED); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponseSoftwareModules(createdSoftwareModules), - HttpStatus.CREATED); + return ResponseEntity.status(CREATED).body(toResponse(createdSoftwareModules)); } @Override public ResponseEntity updateSoftwareModule( @PathVariable("softwareModuleId") final Long softwareModuleId, @RequestBody final MgmtSoftwareModuleRequestBodyPut restSoftwareModule) { + final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); // only description and vendor can be modified @@ -181,16 +193,16 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { } final SoftwareModule updateSoftwareModule = softwareManagement.updateSoftwareModule(module); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(updateSoftwareModule), HttpStatus.OK); + return ResponseEntity.ok(toResponse(updateSoftwareModule)); } @Override public ResponseEntity deleteSoftwareModule(@PathVariable("softwareModuleId") final Long softwareModuleId) { - final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); + final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); softwareManagement.deleteSoftwareModule(module); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.ok().build(); } @Override @@ -218,34 +230,38 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { metaDataPage = softwareManagement.findSoftwareModuleMetadataBySoftwareModuleId(softwareModuleId, pageable); } - return new ResponseEntity<>( - new PagedList<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(metaDataPage.getContent()), - metaDataPage.getTotalElements()), - HttpStatus.OK); + return ResponseEntity + .ok(new PagedList<>(toResponseSwMetadata(metaDataPage.getContent()), metaDataPage.getTotalElements())); } @Override public ResponseEntity getMetadataValue(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("metadataKey") final String metadataKey) { + final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); final SoftwareModuleMetadata findOne = softwareManagement.findSoftwareModuleMetadata(sw, metadataKey); - return ResponseEntity. ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(findOne)); + + return ResponseEntity.ok(toResponseSwMetadata(findOne)); } @Override public ResponseEntity updateMetadata(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("metadataKey") final String metadataKey, @RequestBody final MgmtMetadata metadata) { + final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); final SoftwareModuleMetadata updated = softwareManagement.updateSoftwareModuleMetadata( entityFactory.generateSoftwareModuleMetadata(sw, metadataKey, metadata.getValue())); - return ResponseEntity.ok(MgmtSoftwareModuleMapper.toResponseSwMetadata(updated)); + + return ResponseEntity.ok(toResponseSwMetadata(updated)); } @Override public ResponseEntity deleteMetadata(@PathVariable("softwareModuleId") final Long softwareModuleId, @PathVariable("metadataKey") final String metadataKey) { + final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); softwareManagement.deleteSoftwareModuleMetadata(sw, metadataKey); + return ResponseEntity.ok().build(); } @@ -253,24 +269,25 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi { public ResponseEntity> createMetadata( @PathVariable("softwareModuleId") final Long softwareModuleId, @RequestBody final List metadataRest) { - final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); + final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); final List created = softwareManagement.createSoftwareModuleMetadata( MgmtSoftwareModuleMapper.fromRequestSwMetadata(entityFactory, sw, metadataRest)); - return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponseSwMetadata(created), HttpStatus.CREATED); - + return ResponseEntity.status(CREATED).body(toResponseSwMetadata(created)); } private SoftwareModule findSoftwareModuleWithExceptionIfNotFound(final Long softwareModuleId, final Long artifactId) { + final SoftwareModule module = softwareManagement.findSoftwareModuleById(softwareModuleId); if (module == null) { throw new EntityNotFoundException("SoftwareModule with Id {" + softwareModuleId + "} does not exist"); - } else if (artifactId != null && !module.getLocalArtifact(artifactId).isPresent()) { + } + if (artifactId != null && !module.getLocalArtifact(artifactId).isPresent()) { throw new EntityNotFoundException("Artifact with Id {" + artifactId + "} does not exist"); } + return module; } - } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeMapper.java index 565af7eb8..e14ed27fa 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeMapper.java @@ -11,9 +11,10 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType; import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPost; @@ -25,9 +26,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType; * A mapper which maps repository model to RESTful model representation and * back. * - * - * - * */ final class MgmtSoftwareModuleTypeMapper { @@ -37,13 +35,12 @@ final class MgmtSoftwareModuleTypeMapper { } static List smFromRequest(final EntityFactory entityFactory, - final Iterable smTypesRest) { - final List mappedList = new ArrayList<>(); - - for (final MgmtSoftwareModuleTypeRequestBodyPost smRest : smTypesRest) { - mappedList.add(fromRequest(entityFactory, smRest)); + final Collection smTypesRest) { + if (smTypesRest == null) { + return Collections.emptyList(); } - return mappedList; + + return smTypesRest.stream().map(smRest -> fromRequest(entityFactory, smRest)).collect(Collectors.toList()); } static SoftwareModuleType fromRequest(final EntityFactory entityFactory, @@ -57,20 +54,12 @@ final class MgmtSoftwareModuleTypeMapper { return result; } - static List toTypesResponse(final List types) { - final List response = new ArrayList<>(); - for (final SoftwareModuleType softwareModule : types) { - response.add(toResponse(softwareModule)); + static List toTypesResponse(final Collection types) { + if (types == null) { + return Collections.emptyList(); } - return response; - } - static List toListResponse(final Collection types) { - final List response = new ArrayList<>(); - for (final SoftwareModuleType softwareModule : types) { - response.add(toResponse(softwareModule)); - } - return response; + return types.stream().map(MgmtSoftwareModuleTypeMapper::toResponse).collect(Collectors.toList()); } static MgmtSoftwareModuleType toResponse(final SoftwareModuleType type) { diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResource.java index 353cf16e5..762dd7c1c 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResource.java @@ -72,7 +72,7 @@ public class MgmtSoftwareModuleTypeResource implements MgmtSoftwareModuleTypeRes } final List rest = MgmtSoftwareModuleTypeMapper - .toListResponse(findModuleTypessAll.getContent()); + .toTypesResponse(findModuleTypessAll.getContent()); return new ResponseEntity<>(new PagedList<>(rest, countModulesAll), HttpStatus.OK); } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java index a889edc35..014d0e9e3 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java @@ -13,9 +13,11 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; import java.net.URI; import java.time.ZoneId; -import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction; @@ -92,17 +94,17 @@ public final class MgmtTargetMapper { * the targets * @return the response */ - public static List toResponseWithLinksAndPollStatus(final Iterable targets) { - final List mappedList = new ArrayList<>(); - if (targets != null) { - for (final Target target : targets) { - final MgmtTarget response = toResponse(target); - addPollStatus(target, response); - addTargetLinks(response); - mappedList.add(response); - } + public static List toResponseWithLinksAndPollStatus(final Collection targets) { + if (targets == null) { + return Collections.emptyList(); } - return mappedList; + + return targets.stream().map(target -> { + final MgmtTarget response = toResponse(target); + addPollStatus(target, response); + addTargetLinks(response); + return response; + }).collect(Collectors.toList()); } /** @@ -112,15 +114,12 @@ public final class MgmtTargetMapper { * list of targets * @return the response */ - public static List toResponse(final Iterable targets) { - final List mappedList = new ArrayList<>(); - if (targets != null) { - for (final Target target : targets) { - final MgmtTarget response = toResponse(target); - mappedList.add(response); - } + public static List toResponse(final Collection targets) { + if (targets == null) { + return Collections.emptyList(); } - return mappedList; + + return targets.stream().map(MgmtTargetMapper::toResponse).collect(Collectors.toList()); } /** @@ -173,12 +172,13 @@ public final class MgmtTargetMapper { } static List fromRequest(final EntityFactory entityFactory, - final Iterable targetsRest) { - final List mappedList = new ArrayList<>(); - for (final MgmtTargetRequestBody targetRest : targetsRest) { - mappedList.add(fromRequest(entityFactory, targetRest)); + final Collection targetsRest) { + if (targetsRest == null) { + return Collections.emptyList(); } - return mappedList; + + return targetsRest.stream().map(targetRest -> fromRequest(entityFactory, targetRest)) + .collect(Collectors.toList()); } static Target fromRequest(final EntityFactory entityFactory, final MgmtTargetRequestBody targetRest) { @@ -190,17 +190,12 @@ public final class MgmtTargetMapper { return target; } - static List toActionStatusRestResponse(final List actionStatus) { - final List mappedList = new ArrayList<>(); - - if (actionStatus != null) { - for (final ActionStatus status : actionStatus) { - final MgmtActionStatus response = toResponse(status); - mappedList.add(response); - } + static List toActionStatusRestResponse(final Collection actionStatus) { + if (actionStatus == null) { + return Collections.emptyList(); } - return mappedList; + return actionStatus.stream().map(MgmtTargetMapper::toResponse).collect(Collectors.toList()); } static MgmtAction toResponse(final String targetId, final Action action, final boolean isActive) { @@ -222,14 +217,13 @@ public final class MgmtTargetMapper { return result; } - static List toResponse(final String targetId, final List actions) { - final List mappedList = new ArrayList<>(); - - for (final Action action : actions) { - final MgmtAction response = toResponse(targetId, action, action.isActive()); - mappedList.add(response); + static List toResponse(final String targetId, final Collection actions) { + if (actions == null) { + return Collections.emptyList(); } - return mappedList; + + return actions.stream().map(action -> toResponse(targetId, action, action.isActive())) + .collect(Collectors.toList()); } private static String getNameOfActionStatusType(final Action.Status type) { diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java index 5ddb74314..d0d72be40 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java @@ -11,6 +11,7 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -108,7 +109,7 @@ public class MgmtTargetResource implements MgmtTargetRestApi { @Override public ResponseEntity> createTargets(@RequestBody final List targets) { LOG.debug("creating {} targets", targets.size()); - final Iterable createdTargets = this.targetManagement + final Collection createdTargets = this.targetManagement .createTargets(MgmtTargetMapper.fromRequest(entityFactory, targets)); LOG.debug("{} targets created, return status {}", targets.size(), HttpStatus.CREATED); return new ResponseEntity<>(MgmtTargetMapper.toResponse(createdTargets), HttpStatus.CREATED); diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java index 9d5b0d002..0dd90b35f 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDistributionSetTypeResourceTest.java @@ -494,9 +494,6 @@ public class MgmtDistributionSetTypeResourceTest extends AbstractRestIntegration final SoftwareModuleType testSmType = softwareManagement.createSoftwareModuleType( entityFactory.generateSoftwareModuleType("test123", "TestName123", "Desc123", 5)); - final List types = new ArrayList<>(); - types.add(testType); - // DST does not exist mvc.perform(get("/rest/v1/distributionsettypes/12345678")).andDo(MockMvcResultPrinter.print()) .andExpect(status().isNotFound()); @@ -554,9 +551,8 @@ public class MgmtDistributionSetTypeResourceTest extends AbstractRestIntegration .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - final DistributionSetType missingName = entityFactory.generateDistributionSetType("test123", null, "Desc123"); - mvc.perform(post("/rest/v1/distributionsettypes") - .content(JsonBuilder.distributionSetTypes(Lists.newArrayList(missingName))) + // Missing mandatory field name + mvc.perform(post("/rest/v1/distributionsettypes").content("[{\"description\":\"Desc123\",\"key\":\"test123\"}]") .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java index f5c79fb6c..4f3e12b16 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleResourceTest.java @@ -438,8 +438,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestW SoftwareModule sm = entityFactory.generateSoftwareModule(osType, "name 1", "version 1", null, null); sm = softwareManagement.createSoftwareModule(sm); - final List modules = new ArrayList<>(); - modules.add(sm); + final List modules = Lists.newArrayList(sm); // SM does not exist mvc.perform(get("/rest/v1/softwaremodules/12345678")).andDo(MockMvcResultPrinter.print()) @@ -457,11 +456,10 @@ public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestW .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - final SoftwareModule missingName = entityFactory.generateSoftwareModule(osType, null, "version 1", null, null); - mvc.perform( - post("/rest/v1/softwaremodules").content(JsonBuilder.softwareModules(Lists.newArrayList(missingName))) - .contentType(MediaType.APPLICATION_JSON)) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest()); + mvc.perform(post("/rest/v1/softwaremodules") + .content("[{\"description\":\"Desc123\",\"key\":\"test123\", \"type\":\"os\"}]") + .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isBadRequest()); final SoftwareModule toLongName = entityFactory.generateSoftwareModule(osType, RandomStringUtils.randomAscii(80), "version 1", null, null); diff --git a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java index 8c60815e3..8150eba54 100644 --- a/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java +++ b/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleTypeResourceTest.java @@ -318,8 +318,7 @@ public class MgmtSoftwareModuleTypeResourceTest extends AbstractRestIntegrationT final SoftwareModuleType testType = softwareManagement.createSoftwareModuleType( entityFactory.generateSoftwareModuleType("test123", "TestName123", "Desc123", 5)); - final List types = new ArrayList<>(); - types.add(testType); + final List types = Lists.newArrayList(testType); // SM does not exist mvc.perform(get("/rest/v1/softwaremoduletypes/12345678")).andDo(MockMvcResultPrinter.print()) @@ -337,9 +336,9 @@ public class MgmtSoftwareModuleTypeResourceTest extends AbstractRestIntegrationT .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); - final SoftwareModuleType missingName = entityFactory.generateSoftwareModuleType("test123", null, "Desc123", 5); mvc.perform(post("/rest/v1/softwaremoduletypes") - .content(JsonBuilder.softwareModuleTypes(Lists.newArrayList(missingName))) + .content( + "[{\"description\":\"Desc123\",\"id\":9223372036854775807,\"key\":\"test123\",\"maxAssignments\":5}]") .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isBadRequest()); diff --git a/hawkbit-mgmt-resource/src/test/resources/logback-spring.xml b/hawkbit-mgmt-resource/src/test/resources/logback-spring.xml index 6dd6c94e0..39f7e6bbf 100644 --- a/hawkbit-mgmt-resource/src/test/resources/logback-spring.xml +++ b/hawkbit-mgmt-resource/src/test/resources/logback-spring.xml @@ -23,7 +23,7 @@ - + diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java index 167b6019f..b78d1500d 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ArtifactManagement.java @@ -208,15 +208,16 @@ public interface ArtifactManagement { void deleteLocalArtifact(@NotNull Long id); /** - * Searches for {@link Artifact} with given {@link Identifiable}. + * Searches for {@link LocalArtifact} with given {@link Identifiable}. * * @param id * to search for - * @return found {@link Artifact} or null is it could not be - * found. + * @return found {@link LocalArtifact} or null is it could not + * be found. */ - @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - Artifact findArtifact(@NotNull Long id); + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_CONTROLLER) + LocalArtifact findLocalArtifact(@NotNull Long id); /** * Find by artifact by software module id and filename. diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java index f93b42e48..58d26439d 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java @@ -183,22 +183,6 @@ public interface ControllerManagement { @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) String getPollingTime(); - /** - * An direct access to the security token of an - * {@link Target#getSecurityToken()} without authorization. This is - * necessary to be able to access the security-token without any - * security-context information because the security-token is used for - * authentication. - * - * @param controllerId - * the ID of the controller to retrieve the security token for - * @return the security context of the target, in case no target exists for - * the given controllerId {@code null} is returned - */ - @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.HAS_AUTH_READ_TARGET_SEC_TOKEN) - String getSecurityTokenByControllerId(@NotEmpty String controllerId); - /** * Checks if a given target has currently or has even been assigned to the * given artifact through the action history list. This can e.g. indicate if @@ -218,6 +202,25 @@ public interface ControllerManagement { @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) boolean hasTargetArtifactAssigned(@NotNull String controllerId, @NotNull LocalArtifact localArtifact); + /** + * Checks if a given target has currently or has even been assigned to the + * given artifact through the action history list. This can e.g. indicate if + * a target is allowed to download a given artifact because it has currently + * assigned or had ever been assigned to the target and so it's visible to a + * specific target e.g. for downloading. + * + * @param targetId + * the ID of the target to check + * @param localArtifact + * the artifact to verify if the given target had even been + * assigned to + * @return {@code true} if the given target has currently or had ever a + * relation to the given artifact through the action history, + * otherwise {@code false} + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + boolean hasTargetArtifactAssigned(@NotNull Long targetId, @NotNull LocalArtifact localArtifact); + /** * Registers retrieved status for given {@link Target} and {@link Action} if * it does not exist yet. @@ -300,4 +303,32 @@ public interface ControllerManagement { TargetInfo updateTargetStatus(@NotNull TargetInfo targetInfo, TargetUpdateStatus status, Long lastTargetQuery, URI address); + /** + * Finds {@link Target} based on given controller ID returns found Target + * without details, i.e. NO {@link Target#getTags()} and + * {@link Target#getActions()} possible. + * + * @param controllerId + * to look for. + * @return {@link Target} or {@code null} if it does not exist + * @see Target#getControllerId() + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_SYSTEM_CODE) + Target findByControllerId(@NotEmpty final String controllerId); + + /** + * Finds {@link Target} based on given ID returns found Target without + * details, i.e. NO {@link Target#getTags()} and {@link Target#getActions()} + * possible. + * + * @param targetId + * to look for. + * @return {@link Target} or {@code null} if it does not exist + * @see Target#getId() + */ + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_SYSTEM_CODE) + Target findByTargetId(final long targetId); + } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java index 704bb38e9..171808cb0 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetAssignmentResult.java @@ -57,7 +57,11 @@ public class DistributionSetAssignmentResult extends AssignmentResult { * @return the actionIds */ public List getActions() { - return actions; + if (actions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(actions); } @Override diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java index d8a6f54fe..1de51fad9 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java @@ -205,8 +205,10 @@ public interface DistributionSetManagement { /** * deletes a distribution set meta data entry. * - * @param id - * the ID of the distribution set meta data to delete + * @param distributionSet + * where meta data has to be deleted + * @param key + * of the meta data element */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) void deleteDistributionSetMetadata(@NotNull final DistributionSet distributionSet, @NotNull final String key); @@ -429,7 +431,7 @@ public interface DistributionSetManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - DistributionSetType findDistributionSetTypeByKey(@NotNull String key); + DistributionSetType findDistributionSetTypeByKey(@NotEmpty String key); /** * @param name @@ -469,15 +471,16 @@ public interface DistributionSetManagement { /** * finds a single distribution set meta data by its id. * - * @param id - * the id of the distribution set meta data containing the meta - * data key and the ID of the distribution set + * @param distributionSet + * where meta data has to rind + * @param key + * of the meta data element * @return the found DistributionSetMetadata or {@code null} if not exits * @throws EntityNotFoundException * in case the meta data does not exists for the given key */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - DistributionSetMetadata findOne(@NotNull DistributionSet distributionSet, @NotNull String key); + DistributionSetMetadata findOne(@NotNull DistributionSet distributionSet, @NotEmpty String key); /** * Checks if a {@link DistributionSet} is currently in use by a target in diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java index 87919729e..925351807 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/EntityFactory.java @@ -11,6 +11,8 @@ package org.eclipse.hawkbit.repository; import java.util.Collection; import java.util.concurrent.TimeUnit; +import javax.validation.constraints.NotNull; + import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; @@ -64,7 +66,7 @@ public interface EntityFactory { * * @return {@link ActionStatus} object */ - ActionStatus generateActionStatus(Action action, Status status, Long occurredAt); + ActionStatus generateActionStatus(@NotNull Action action, @NotNull Status status, Long occurredAt); /** * Generates an {@link ActionStatus} object without persisting it. @@ -81,7 +83,7 @@ public interface EntityFactory { * * @return {@link ActionStatus} object */ - ActionStatus generateActionStatus(Action action, final Status status, Long occurredAt, + ActionStatus generateActionStatus(@NotNull Action action, @NotNull Status status, Long occurredAt, final Collection messages); /** @@ -99,7 +101,8 @@ public interface EntityFactory { * * @return {@link ActionStatus} object */ - ActionStatus generateActionStatus(Action action, Status status, Long occurredAt, final String message); + ActionStatus generateActionStatus(@NotNull Action action, @NotNull Status status, Long occurredAt, + final String message); /** * Generates an empty {@link DistributionSet} without persisting it. @@ -124,8 +127,8 @@ public interface EntityFactory { * * @return {@link DistributionSet} object */ - DistributionSet generateDistributionSet(String name, String version, String description, DistributionSetType type, - Collection moduleList); + DistributionSet generateDistributionSet(@NotNull String name, @NotNull String version, String description, + @NotNull DistributionSetType type, Collection moduleList); /** * Generates an empty {@link DistributionSetMetadata} element without @@ -148,7 +151,8 @@ public interface EntityFactory { * * @return {@link DistributionSetMetadata} object */ - DistributionSetMetadata generateDistributionSetMetadata(DistributionSet distributionSet, String key, String value); + DistributionSetMetadata generateDistributionSetMetadata(@NotNull DistributionSet distributionSet, + @NotNull String key, String value); /** * Generates an empty {@link DistributionSetTag} without persisting it. @@ -164,7 +168,7 @@ public interface EntityFactory { * of the tag * @return {@link DistributionSetTag} object */ - DistributionSetTag generateDistributionSetTag(String name); + DistributionSetTag generateDistributionSetTag(@NotNull String name); /** * Generates a {@link DistributionSetTag} without persisting it. @@ -177,7 +181,7 @@ public interface EntityFactory { * of the tag * @return {@link DistributionSetTag} object */ - DistributionSetTag generateDistributionSetTag(String name, String description, String colour); + DistributionSetTag generateDistributionSetTag(@NotNull String name, String description, String colour); /** * Generates an empty {@link DistributionSetType} without persisting it. @@ -198,7 +202,7 @@ public interface EntityFactory { * * @return {@link DistributionSetType} object */ - DistributionSetType generateDistributionSetType(String key, String name, String description); + DistributionSetType generateDistributionSetType(@NotNull String key, @NotNull String name, String description); /** * Generates an empty {@link Rollout} without persisting it. @@ -237,8 +241,8 @@ public interface EntityFactory { * * @return {@link SoftwareModule} object */ - SoftwareModule generateSoftwareModule(SoftwareModuleType type, String name, String version, String description, - String vendor); + SoftwareModule generateSoftwareModule(@NotNull SoftwareModuleType type, @NotNull String name, + @NotNull String version, String description, String vendor); /** * Generates an empty {@link SoftwareModuleMetadata} pair without persisting @@ -260,7 +264,8 @@ public interface EntityFactory { * * @return {@link SoftwareModuleMetadata} object */ - SoftwareModuleMetadata generateSoftwareModuleMetadata(SoftwareModule softwareModule, String key, String value); + SoftwareModuleMetadata generateSoftwareModuleMetadata(@NotNull SoftwareModule softwareModule, @NotNull String key, + String value); /** * Generates an empty {@link SoftwareModuleType} without persisting it. @@ -283,7 +288,8 @@ public interface EntityFactory { * * @return {@link SoftwareModuleType} object */ - SoftwareModuleType generateSoftwareModuleType(String key, String name, String description, int maxAssignments); + SoftwareModuleType generateSoftwareModuleType(@NotNull String key, @NotNull String name, String description, + int maxAssignments); /** * Generates an empty {@link Target} without persisting it. @@ -307,7 +313,7 @@ public interface EntityFactory { * * @return {@link Target} object */ - Target generateTarget(@NotEmpty String controllerID, @NotEmpty String securityToken); + Target generateTarget(@NotEmpty String controllerID, String securityToken); /** * Generates an empty {@link TargetFilterQuery} without persisting it. @@ -330,7 +336,7 @@ public interface EntityFactory { * of the tag * @return {@link TargetTag} object */ - TargetTag generateTargetTag(String name); + TargetTag generateTargetTag(@NotNull String name); /** * Generates a {@link TargetTag} without persisting it. @@ -343,7 +349,7 @@ public interface EntityFactory { * of the tag * @return {@link TargetTag} object */ - TargetTag generateTargetTag(String name, String description, String colour); + TargetTag generateTargetTag(@NotNull String name, String description, String colour); /** * Generates an empty {@link LocalArtifact} without persisting it. diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java index dfe05100e..2746686a7 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java @@ -155,11 +155,13 @@ public interface SoftwareManagement { /** * deletes a software module meta data entry. * - * @param id - * the ID of the software module meta data to delete + * @param softwareModule + * where meta data has to be deleted + * @param key + * of the metda data element */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) - void deleteSoftwareModuleMetadata(@NotNull SoftwareModule softwareModule, @NotNull String key); + void deleteSoftwareModuleMetadata(@NotNull SoftwareModule softwareModule, @NotEmpty String key); /** * Deletes {@link SoftwareModule}s which is any if the given ids. @@ -251,9 +253,10 @@ public interface SoftwareManagement { /** * finds a single software module meta data by its id. * - * @param id - * the id of the software module meta data containing the meta - * data key and the ID of the software module + * @param softwareModule + * where meta data has to be found + * @param key + * of the meta data element * @return the found SoftwareModuleMetadata or {@code null} if not exits * @throws EntityNotFoundException * in case the meta data does not exists for the given key @@ -280,8 +283,8 @@ public interface SoftwareManagement { * * @param softwareModuleId * the software module id to retrieve the meta data from - * @param spec - * the specification to filter the result + * @param rsqlParam + * filter definition in RSQL syntax * @param pageable * the page request to page the result * @return a paged result of all meta data entries for a given software @@ -346,8 +349,8 @@ public interface SoftwareManagement { /** * Retrieves all {@link SoftwareModule}s with a given specification. * - * @param spec - * the specification to filter the software modules + * @param rsqlParam + * filter definition in RSQL syntax * @param pageable * pagination parameter * @return the found {@link SoftwareModule}s @@ -392,7 +395,7 @@ public interface SoftwareManagement { * {@link SoftwareModuleType#getKey()} */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - SoftwareModuleType findSoftwareModuleTypeByKey(@NotNull String key); + SoftwareModuleType findSoftwareModuleTypeByKey(@NotEmpty String key); /** * @@ -415,8 +418,8 @@ public interface SoftwareManagement { /** * Retrieves all {@link SoftwareModuleType}s with a given specification. * - * @param spec - * the specification to filter the software modules types + * @param rsqlParam + * filter definition in RSQL syntax * @param pageable * pagination parameter * @return the found {@link SoftwareModuleType}s diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java index cd414b09a..131f63107 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java @@ -63,7 +63,8 @@ public interface SystemManagement { */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR + SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) + + SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.IS_CONTROLLER) TenantMetaData getTenantMetadata(); /** @@ -93,4 +94,14 @@ public interface SystemManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) TenantMetaData updateTenantMetadata(@NotNull TenantMetaData metaData); + /** + * Returns {@link TenantMetaData} of given tenant ID. + * + * @param tenantId + * to retrieve data for + * @return {@link TenantMetaData} of given tenant + */ + @PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE) + TenantMetaData getTenantMetadata(@NotNull Long tenantId); + } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/RolloutGroupCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/RolloutGroupCreatedEvent.java index 41f08c91d..47fb6b39e 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/RolloutGroupCreatedEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/RolloutGroupCreatedEvent.java @@ -34,6 +34,8 @@ public class RolloutGroupCreatedEvent extends AbstractDistributedEvent { * the revision of the event * @param rolloutId * the ID of the rollout the group has been created + * @param rolloutGroupId + * identifier of this group * @param totalRolloutGroup * the total number of rollout groups for this rollout * @param createdRolloutGroup diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/TargetAssignDistributionSetEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/TargetAssignDistributionSetEvent.java index 586a4b9d2..e87d49b57 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/TargetAssignDistributionSetEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/eventbus/event/TargetAssignDistributionSetEvent.java @@ -8,11 +8,11 @@ */ package org.eclipse.hawkbit.repository.eventbus.event; -import java.net.URI; import java.util.Collection; import org.eclipse.hawkbit.eventbus.event.DefaultEvent; import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.repository.model.Target; /** * Event that gets sent when a distribution set gets assigned to a target. @@ -21,10 +21,8 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; public class TargetAssignDistributionSetEvent extends DefaultEvent { private final Collection softwareModules; - private final String controllerId; + private final Target target; private final Long actionId; - private final URI targetAdress; - private final String targetToken; /** * Creates a new {@link TargetAssignDistributionSetEvent}. @@ -33,26 +31,19 @@ public class TargetAssignDistributionSetEvent extends DefaultEvent { * the revision of the event * @param tenant * the tenant of the event - * @param controllerId - * the ID of the controller + * @param target + * the assigned {@link Target} * @param actionId * the action id of the assignment * @param softwareModules * the software modules which have been assigned to the target - * @param targetAdress - * the targetAdress of the target - * @param targetToken - * the authentication token of the target */ - public TargetAssignDistributionSetEvent(final long revision, final String tenant, final String controllerId, - final Long actionId, final Collection softwareModules, final URI targetAdress, - final String targetToken) { + public TargetAssignDistributionSetEvent(final long revision, final String tenant, final Target target, + final Long actionId, final Collection softwareModules) { super(revision, tenant); - this.controllerId = controllerId; + this.target = target; this.actionId = actionId; this.softwareModules = softwareModules; - this.targetAdress = targetAdress; - this.targetToken = targetToken; } /** @@ -63,11 +54,11 @@ public class TargetAssignDistributionSetEvent extends DefaultEvent { } /** - * @return the controllerId of the Target which has been assigned to the - * distribution set + * @return the {@link Target} which has been assigned to the distribution + * set */ - public String getControllerId() { - return controllerId; + public Target getTarget() { + return target; } /** @@ -76,12 +67,4 @@ public class TargetAssignDistributionSetEvent extends DefaultEvent { public Collection getSoftwareModules() { return softwareModules; } - - public URI getTargetAdress() { - return targetAdress; - } - - public String getTargetToken() { - return targetToken; - } } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java index e83108fe8..2b5daed2e 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ActionStatus.java @@ -47,10 +47,10 @@ public interface ActionStatus extends TenantAwareBaseEntity { * @return current {@link Status#DOWNLOAD} progress if known by the update * server. */ - int getDownloadProgressPercent(); + short getDownloadProgressPercent(); /** - * @return list of message entries that can be added to the + * @return immutable list of message entries that in the * {@link ActionStatus}. */ List getMessages(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java index cc726839d..e0a142b43 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java @@ -37,6 +37,6 @@ public interface Artifact extends TenantAwareBaseEntity { /** * @return size of the artifact in bytes. */ - Long getSize(); + long getSize(); } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java index b23b3f0fe..9215fe46f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java @@ -60,7 +60,7 @@ public class AssignedSoftwareModule implements Serializable { final int prime = 31; int result = 1; result = prime * result + (assigned ? 1231 : 1237); - result = prime * result + (softwareModule == null ? 0 : softwareModule.hashCode()); + result = prime * result + ((softwareModule == null) ? 0 : softwareModule.hashCode()); return result; } @@ -72,7 +72,7 @@ public class AssignedSoftwareModule implements Serializable { if (obj == null) { return false; } - if (!(obj instanceof AssignedSoftwareModule)) { + if (getClass() != obj.getClass()) { return false; } final AssignedSoftwareModule other = (AssignedSoftwareModule) obj; diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignmentResult.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignmentResult.java index 9468c8a88..6333a61fc 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignmentResult.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignmentResult.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.model; +import java.util.Collections; import java.util.List; /** @@ -82,14 +83,22 @@ public class AssignmentResult { * @return {@link List} of assigned entity. */ public List getAssignedEntity() { - return assignedEntity; + if (assignedEntity == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(assignedEntity); } /** * @return {@link List} of unassigned entity. */ public List getUnassignedEntity() { - return unassignedEntity; + if (unassignedEntity == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(unassignedEntity); } } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/BaseEntity.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/BaseEntity.java index 2a9948bc5..c6717d021 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/BaseEntity.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/BaseEntity.java @@ -44,6 +44,6 @@ public interface BaseEntity extends Serializable, Identifiable { /** * @return version of the {@link BaseEntity}. */ - long getOptLockRevision(); + int getOptLockRevision(); } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java index 5747b38f3..b0bff56cb 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSet.java @@ -26,10 +26,25 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement; public interface DistributionSet extends NamedVersionedEntity { /** - * @return {@link Set} of assigned {@link DistributionSetTag}s. + * @return immutable {@link Set} of assigned {@link DistributionSetTag}s. */ Set getTags(); + /** + * @param tag + * to add + * @return true if tag could be added sucessfully (i.e. was not + * already in the list). + */ + boolean addTag(final DistributionSetTag tag); + + /** + * @param tag + * to remove + * @return true if tag was in the list and removed + */ + boolean removeTag(final DistributionSetTag tag); + /** * @return true if the set is deleted and only kept for history * purposes. diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java index 05089d138..9507e29b2 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetTag.java @@ -17,8 +17,8 @@ import java.util.List; public interface DistributionSetTag extends Tag { /** - * @return {@link List} of {@link DistributionSet}s this {@link Tag} is - * assigned to. + * @return immutable {@link List} of {@link DistributionSet}s this + * {@link Tag} is assigned to. */ List getAssignedToDistributionSet(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java index cf5da40b2..57420bbad 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java @@ -27,15 +27,15 @@ public interface DistributionSetType extends NamedEntity { boolean isDeleted(); /** - * @return set of {@link SoftwareModuleType}s that need to be in a + * @return immutable set of {@link SoftwareModuleType}s that need to be in a * {@link DistributionSet} of this type to be * {@link DistributionSet#isComplete()}. */ Set getMandatoryModuleTypes(); /** - * @return set of optional {@link SoftwareModuleType}s that can be in a - * {@link DistributionSet} of this type. + * @return immutable set of optional {@link SoftwareModuleType}s that can be + * in a {@link DistributionSet} of this type. */ Set getOptionalModuleTypes(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/EntityInterceptor.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/EntityInterceptor.java index b6fff3f91..9ed069c66 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/EntityInterceptor.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/EntityInterceptor.java @@ -11,6 +11,9 @@ package org.eclipse.hawkbit.repository.model; /** * Interface for the entity interceptor lifecycle. */ +// Exception squid:EmptyStatementUsageCheck - don't want to force users to +// impelemnt all methods +@SuppressWarnings("squid:EmptyStatementUsageCheck") public interface EntityInterceptor { /** diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java index 37185a8e2..72d7e7c53 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Rollout.java @@ -38,7 +38,7 @@ public interface Rollout extends NamedEntity { void setDistributionSet(DistributionSet distributionSet); /** - * @return list of deployment groups of the rollout. + * @return immutable list of deployment groups of the rollout. */ List getRolloutGroups(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java index 3180fdb6a..286085eba 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java @@ -155,7 +155,7 @@ public interface RolloutGroup extends NamedEntity { /** * @return the total amount of targets containing in this group */ - long getTotalTargets(); + int getTotalTargets(); /** * @return the totalTargetCountStatus diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java index 3c3a24fd0..e3bf1468f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java @@ -11,6 +11,10 @@ package org.eclipse.hawkbit.repository.model; import java.util.List; import java.util.Optional; +/** + * Software package as sub element of a {@link DistributionSet}. + * + */ public interface SoftwareModule extends NamedVersionedEntity { /** @@ -40,12 +44,12 @@ public interface SoftwareModule extends NamedVersionedEntity { Optional getLocalArtifactByFilename(String fileName); /** - * @return the artifacts + * @return immutable list of all artifacts */ List getArtifacts(); /** - * @return local artifacts only + * @return immutable list of local artifacts only */ List getLocalArtifacts(); @@ -104,7 +108,8 @@ public interface SoftwareModule extends NamedVersionedEntity { List getMetadata(); /** - * @return the assignedTo + * @return immutable list of {@link DistributionSet}s the module is assigned + * to */ List getAssignedTo(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java index c09b28a2f..c07a8c224 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Target.java @@ -32,12 +32,12 @@ public interface Target extends NamedEntity { String getControllerId(); /** - * @return assigned {@link TargetTag}s. + * @return immutable set of assigned {@link TargetTag}s. */ Set getTags(); /** - * @return {@link Action} history of the {@link Target}. + * @return immutable {@link Action} history of the {@link Target}. */ List getActions(); @@ -64,4 +64,19 @@ public interface Target extends NamedEntity { */ void setSecurityToken(String token); + /** + * @param tag + * to add + * @return true if tag could be added sucessfully (i.e. was not + * already in the list). + */ + public boolean addTag(TargetTag tag); + + /** + * @param tag + * to remove + * @return true if tag was in the list and removed + */ + public boolean removeTag(TargetTag tag); + } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java index 2eb2f05c7..76a975f75 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetInfo.java @@ -36,7 +36,8 @@ public interface TargetInfo extends Serializable { /** * @return time in {@link TimeUnit#MILLISECONDS} GMT when the {@link Target} - * polled the server the last time. + * polled the server the last time or null if target + * has never queried yet. */ Long getLastTargetQuery(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTag.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTag.java index f222e9e90..afe0ff59a 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTag.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetTag.java @@ -17,7 +17,7 @@ import java.util.List; public interface TargetTag extends Tag { /** - * @return {@link List} of targets assigned to this {@link Tag}. + * @return immutable {@link List} of targets assigned to this {@link Tag}. */ List getAssignedToTargets(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/pom.xml b/hawkbit-repository/hawkbit-repository-jpa/pom.xml index 3a66a0a9d..9f7a0bbb7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/pom.xml +++ b/hawkbit-repository/hawkbit-repository-jpa/pom.xml @@ -89,6 +89,10 @@ cz.jirutka.rsql rsql-parser + + org.apache.commons + commons-collections4 + diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java index c38612095..282de58cd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit; -import java.util.HashMap; import java.util.Map; import org.eclipse.hawkbit.repository.ArtifactManagement; @@ -75,6 +74,7 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; +import com.google.common.collect.Maps; import com.google.common.eventbus.EventBus; /** @@ -205,7 +205,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { @Override protected Map getVendorProperties() { - final Map properties = new HashMap<>(); + final Map properties = Maps.newHashMapWithExpectedSize(5); // Turn off dynamic weaving to disable LTW lookup in static weaving mode properties.put("eclipselink.weaving", "false"); // needed for reports diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java index 8989a5a41..b39d760c3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/ActionRepository.java @@ -30,6 +30,7 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; @@ -103,7 +104,7 @@ public interface ActionRepository extends BaseEntityRepository, * @return the found {@link Action} */ @EntityGraph(value = "Action.ds", type = EntityGraphType.LOAD) - Optional findFirstByTargetAndActiveOrderByIdAsc(final JpaTarget target, boolean active); + Optional findFirstByTargetAndActive(final Sort sort, final JpaTarget target, boolean active); /** * Retrieves latest {@link UpdateAction} for given target and diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java index 3c94d885b..5e16b7cd9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java @@ -29,7 +29,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaExternalArtifactProvider; import org.eclipse.hawkbit.repository.jpa.model.JpaLocalArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification; -import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.ExternalArtifact; import org.eclipse.hawkbit.repository.model.ExternalArtifactProvider; import org.eclipse.hawkbit.repository.model.LocalArtifact; @@ -194,7 +193,7 @@ public class JpaArtifactManagement implements ArtifactManagement { } @Override - public Artifact findArtifact(final Long id) { + public LocalArtifact findLocalArtifact(final Long id) { return localArtifactRepository.findOne(id); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java index 41ad6f179..a7da485ad 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java @@ -9,7 +9,7 @@ package org.eclipse.hawkbit.repository.jpa; import java.net.URI; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -54,6 +54,8 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Direction; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.Modifying; import org.springframework.transaction.annotation.Isolation; @@ -156,6 +158,15 @@ public class JpaControllerManagement implements ControllerManagement { return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, localArtifact)) > 0; } + @Override + public boolean hasTargetArtifactAssigned(final Long targetId, final LocalArtifact localArtifact) { + final Target target = targetRepository.findOne(targetId); + if (target == null) { + return false; + } + return actionRepository.count(ActionSpecifications.hasTargetAssignedArtifact(target, localArtifact)) > 0; + } + @Override public List findActiveActionByTarget(final Target target) { return actionRepository.findByTargetAndActiveOrderByIdAsc((JpaTarget) target, true); @@ -163,12 +174,15 @@ public class JpaControllerManagement implements ControllerManagement { @Override public Optional findOldestActiveActionByTarget(final Target target) { - return actionRepository.findFirstByTargetAndActiveOrderByIdAsc((JpaTarget) target, true); + // used in favorite to findFirstByTargetAndActiveOrderByIdAsc due to + // DATAJPA-841 issue. + return actionRepository.findFirstByTargetAndActive(new Sort(Direction.ASC, "id"), (JpaTarget) target, true); } @Override public List findSoftwareModulesByDistributionSet(final DistributionSet distributionSet) { - return new ArrayList<>(softwareModuleRepository.findByAssignedTo((JpaDistributionSet) distributionSet)); + return Collections + .unmodifiableList(softwareModuleRepository.findByAssignedTo((JpaDistributionSet) distributionSet)); } @Override @@ -451,12 +465,6 @@ public class JpaControllerManagement implements ControllerManagement { return actionStatusRepository.save((JpaActionStatus) statusMessage); } - @Override - public String getSecurityTokenByControllerId(final String controllerId) { - final Target target = targetRepository.findByControllerId(controllerId); - return target != null ? target.getSecurityToken() : null; - } - @Override @Modifying @Transactional(isolation = Isolation.READ_UNCOMMITTED) @@ -470,4 +478,14 @@ public class JpaControllerManagement implements ControllerManagement { cacheWriteNotify.downloadProgress(statusId, requestedBytes, shippedBytesSinceLast, shippedBytesOverall); } + @Override + public Target findByControllerId(final String controllerId) { + return targetRepository.findByControllerId(controllerId); + } + + @Override + public Target findByTargetId(final long targetId) { + return targetRepository.findOne(targetId); + } + } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 5a2c6fdda..f97a2d178 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -352,14 +352,13 @@ public class JpaDeploymentManagement implements DeploymentManagement { private void assignDistributionSetEvent(final JpaTarget target, final Long actionId, final List modules) { ((JpaTargetInfo) target.getTargetInfo()).setUpdateStatus(TargetUpdateStatus.PENDING); - final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken()); + @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection softwareModules = (Collection) modules; afterCommit.afterCommit(() -> { eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo())); - eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(), - target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress(), - targetSecurityToken)); + eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(), target, + actionId, softwareModules)); }); } @@ -593,7 +592,7 @@ public class JpaDeploymentManagement implements DeploymentManagement { multiselect.where(cb.equal(actionRoot.get(JpaAction_.target), target)); multiselect.orderBy(cb.desc(actionRoot.get(JpaAction_.id))); multiselect.groupBy(actionRoot.get(JpaAction_.id)); - return new ArrayList<>(entityManager.createQuery(multiselect).getResultList()); + return Collections.unmodifiableList(entityManager.createQuery(multiselect).getResultList()); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java index e170ccad7..994c880e4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java @@ -15,7 +15,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -67,6 +66,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import com.google.common.base.Strings; +import com.google.common.collect.Lists; import com.google.common.eventbus.EventBus; /** @@ -126,26 +126,24 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { final DistributionSetTag myTag = tagManagement.findDistributionSetTag(tagName); DistributionSetTagAssignmentResult result; - final List toBeChangedDSs = new ArrayList<>(); - for (final JpaDistributionSet set : sets) { - if (set.getTags().add(myTag)) { - toBeChangedDSs.add(set); - } - } + + final List toBeChangedDSs = sets.stream().filter(set -> set.addTag(myTag)) + .collect(Collectors.toList()); // un-assignment case if (toBeChangedDSs.isEmpty()) { for (final JpaDistributionSet set : sets) { - if (set.getTags().remove(myTag)) { + if (set.removeTag(myTag)) { toBeChangedDSs.add(set); } } result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), 0, toBeChangedDSs.size(), Collections.emptyList(), - new ArrayList<>(distributionSetRepository.save(toBeChangedDSs)), myTag); + Collections.unmodifiableList(distributionSetRepository.save(toBeChangedDSs)), myTag); } else { result = new DistributionSetTagAssignmentResult(dsIds.size() - toBeChangedDSs.size(), toBeChangedDSs.size(), - 0, new ArrayList<>(distributionSetRepository.save(toBeChangedDSs)), Collections.emptyList(), myTag); + 0, Collections.unmodifiableList(distributionSetRepository.save(toBeChangedDSs)), + Collections.emptyList(), myTag); } final DistributionSetTagAssignmentResult resultAssignment = result; @@ -174,8 +172,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Modifying @Transactional(isolation = Isolation.READ_UNCOMMITTED) public void deleteDistributionSet(final Long... distributionSetIDs) { - final List toHardDelete = new ArrayList<>(); - final List assigned = distributionSetRepository .findAssignedToTargetDistributionSetsById(distributionSetIDs); assigned.addAll(distributionSetRepository.findAssignedToRolloutDistributionSetsById(distributionSetIDs)); @@ -186,13 +182,10 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { } // mark the rest as hard delete - for (final Long setId : distributionSetIDs) { - if (!assigned.contains(setId)) { - toHardDelete.add(setId); - } - } + final List toHardDelete = Arrays.stream(distributionSetIDs).filter(setId -> !assigned.contains(setId)) + .collect(Collectors.toList()); - // hard delete the rest if exixts + // hard delete the rest if exists if (!toHardDelete.isEmpty()) { // don't give the delete statement an empty list, JPA/Oracle cannot // handle the empty list @@ -239,7 +232,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection toSave = (Collection) distributionSets; - return new ArrayList<>(distributionSetRepository.save(toSave)); + return Collections.unmodifiableList(distributionSetRepository.save(toSave)); } @Override @@ -292,7 +285,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { } private static Page convertDsTPage(final Page findAll) { - return new PageImpl<>(new ArrayList<>(findAll.getContent())); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent())); } @Override @@ -310,7 +303,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { private static Page convertDsPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } /** @@ -333,7 +326,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Override public Page findDistributionSetsByDeletedAndOrCompleted(final Pageable pageReq, final Boolean deleted, final Boolean complete) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(2); if (deleted != null) { final Specification spec = DistributionSetSpecification.isDeleted(deleted); @@ -354,11 +347,12 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { final Specification spec = RSQLUtility.parse(rsqlParam, DistributionSetFields.class); - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(2); if (deleted != null) { specList.add(DistributionSetSpecification.isDeleted(deleted)); } specList.add(spec); + return convertDsPage(findByCriteriaAPI(pageReq, specList), pageReq); } @@ -383,7 +377,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { final Page findDistributionSetsByFilters = findDistributionSetsByFilters(pageable, dsFilterWithNoTargetLinked); - final List resultSet = new LinkedList<>(findDistributionSetsByFilters.getContent()); + final List resultSet = new ArrayList<>(findDistributionSetsByFilters.getContent()); int orderIndex = 0; if (installedDS != null) { final boolean remove = resultSet.remove(installedDS); @@ -414,18 +408,15 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Override public List findDistributionSetsAll(final Collection dist) { - return new ArrayList<>(distributionSetRepository.findAll(DistributionSetSpecification.byIds(dist))); + return Collections + .unmodifiableList(distributionSetRepository.findAll(DistributionSetSpecification.byIds(dist))); } @Override public Long countDistributionSetsAll() { - - final List> specList = new LinkedList<>(); - final Specification spec = DistributionSetSpecification.isDeleted(Boolean.FALSE); - specList.add(spec); - return distributionSetRepository.count(SpecificationsBuilder.combineWithAnd(specList)); + return distributionSetRepository.count(SpecificationsBuilder.combineWithAnd(Lists.newArrayList(spec))); } @Override @@ -555,7 +546,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @Override public List findDistributionSetMetadataByDistributionSetId(final Long distributionSetId) { - return new ArrayList<>(distributionSetMetadataRepository + return Collections.unmodifiableList(distributionSetMetadataRepository .findAll((Specification) (root, query, cb) -> cb.equal( root.get(JpaDistributionSetMetadata_.distributionSet).get(JpaDistributionSet_.id), distributionSetId))); @@ -579,7 +570,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { private static Page convertMdPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override @@ -604,7 +595,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { private static List> buildDistributionSetSpecifications( final DistributionSetFilter distributionSetFilter) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(7); Specification spec; @@ -705,9 +696,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { public List assignTag(final Collection dsIds, final DistributionSetTag tag) { final List allDs = findDistributionSetListWithDetails(dsIds); - allDs.forEach(ds -> ds.getTags().add(tag)); + allDs.forEach(ds -> ds.addTag(tag)); - final List save = new ArrayList<>(distributionSetRepository.save(allDs)); + final List save = Collections.unmodifiableList(distributionSetRepository.save(allDs)); afterCommit.afterCommit(() -> { @@ -727,7 +718,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection distributionSets = (Collection) tag.getAssignedToDistributionSet(); - return new ArrayList<>(unAssignTag(distributionSets, tag)); + return Collections.unmodifiableList(unAssignTag(distributionSets, tag)); } @Override @@ -741,7 +732,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { private List unAssignTag(final Collection distributionSets, final DistributionSetTag tag) { - distributionSets.forEach(ds -> ds.getTags().remove(tag)); + distributionSets.forEach(ds -> ds.removeTag(tag)); return distributionSetRepository.save(distributionSets); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java index 51b59f08f..1bc8063ec 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaEntityFactory.java @@ -43,11 +43,13 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetTag; +import org.springframework.validation.annotation.Validated; /** * JPA Implementation of {@link EntityFactory}. * */ +@Validated public class JpaEntityFactory implements EntityFactory { @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java index 9f63204b1..eba63b6c6 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -81,11 +81,11 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { } private static Page convertPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Page convertTPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override @@ -114,7 +114,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { for (final RolloutGroup rolloutGroup : rolloutGroups) { final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus( - allStatesForRollout.get(rolloutGroup.getId()), rolloutGroup.getTotalTargets()); + allStatesForRollout.get(rolloutGroup.getId()), Long.valueOf(rolloutGroup.getTotalTargets())); rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); } @@ -128,7 +128,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { .getStatusCountByRolloutGroupId(rolloutGroupId); final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems, - rolloutGroup.getTotalTargets()); + Long.valueOf(rolloutGroup.getTotalTargets())); rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); return rolloutGroup; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java index 3a903ea89..1cb763f3a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -140,11 +140,11 @@ public class JpaRolloutManagement implements RolloutManagement { } private static Page convertPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Slice convertPage(final Slice findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, 0); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0); } @Override @@ -651,7 +651,7 @@ public class JpaRolloutManagement implements RolloutManagement { @Override public float getFinishedPercentForRunningGroup(final Long rolloutId, final RolloutGroup rolloutGroup) { - final Long totalGroup = rolloutGroup.getTotalTargets(); + final int totalGroup = rolloutGroup.getTotalTargets(); final Long finished = actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rolloutId, rolloutGroup.getId(), Action.Status.FINISHED); if (totalGroup == 0) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java index 3f8c1a54b..401c20895 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareManagement.java @@ -11,12 +11,15 @@ package org.eclipse.hawkbit.repository.jpa; import static com.google.common.base.Preconditions.checkNotNull; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; @@ -65,6 +68,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import com.google.common.base.Strings; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; /** @@ -166,7 +170,7 @@ public class JpaSoftwareManagement implements SoftwareManagement { @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection jpaCast = (Collection) swModules; - return new ArrayList<>(softwareModuleRepository.save(jpaCast)); + return Collections.unmodifiableList(softwareModuleRepository.save(jpaCast)); } @Override @@ -185,22 +189,22 @@ public class JpaSoftwareManagement implements SoftwareManagement { private static Slice convertSmPage(final Slice findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, 0); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0); } private static Page convertSmPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Page convertSmMdPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override public Long countSoftwareModulesByType(final SoftwareModuleType type) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(2); Specification spec = SoftwareModuleSpecification.equalType((JpaSoftwareModuleType) type); specList.add(spec); @@ -277,7 +281,7 @@ public class JpaSoftwareManagement implements SoftwareManagement { @Override public Slice findSoftwareModulesAll(final Pageable pageable) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(2); Specification spec = SoftwareModuleSpecification.isDeletedFalse(); specList.add(spec); @@ -296,13 +300,9 @@ public class JpaSoftwareManagement implements SoftwareManagement { @Override public Long countSoftwareModulesAll() { - - final List> specList = new ArrayList<>(); - final Specification spec = SoftwareModuleSpecification.isDeletedFalse(); - specList.add(spec); - return countSwModuleByCriteriaAPI(specList); + return countSwModuleByCriteriaAPI(Lists.newArrayList(spec)); } @Override @@ -327,20 +327,20 @@ public class JpaSoftwareManagement implements SoftwareManagement { private static Page convertSmTPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) public List findSoftwareModulesById(final Collection ids) { - return new ArrayList<>(softwareModuleRepository.findByIdIn(ids)); + return Collections.unmodifiableList(softwareModuleRepository.findByIdIn(ids)); } @Override public Slice findSoftwareModuleByFilters(final Pageable pageable, final String searchText, final SoftwareModuleType type) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(4); Specification spec = SoftwareModuleSpecification.isDeletedFalse(); specList.add(spec); @@ -438,7 +438,7 @@ public class JpaSoftwareManagement implements SoftwareManagement { private static List> buildSpecificationList(final String searchText, final JpaSoftwareModuleType type) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(3); if (!Strings.isNullOrEmpty(searchText)) { specList.add(SoftwareModuleSpecification.likeNameOrVersion(searchText)); } @@ -452,18 +452,15 @@ public class JpaSoftwareManagement implements SoftwareManagement { private Predicate[] specificationsToPredicate(final List> specifications, final Root root, final CriteriaQuery query, final CriteriaBuilder cb, final Predicate... additionalPredicates) { - final List predicates = new ArrayList<>(); - specifications.forEach(spec -> predicates.add(spec.toPredicate(root, query, cb))); - for (final Predicate predicate : additionalPredicates) { - predicates.add(predicate); - } - return predicates.toArray(new Predicate[predicates.size()]); + + return Stream.concat(specifications.stream().map(spec -> spec.toPredicate(root, query, cb)), + Arrays.stream(additionalPredicates)).toArray(Predicate[]::new); } @Override public Long countSoftwareModuleByFilters(final String searchText, final SoftwareModuleType type) { - final List> specList = new ArrayList<>(); + final List> specList = new ArrayList<>(3); Specification spec = SoftwareModuleSpecification.isDeletedFalse(); specList.add(spec); @@ -573,7 +570,7 @@ public class JpaSoftwareManagement implements SoftwareManagement { checkAndThrowAlreadyExistsIfSoftwareModuleMetadataExists(softwareModuleMetadata.getId()); } metadata.forEach(m -> entityManager.merge((JpaSoftwareModule) m.getSoftwareModule()).setLastModifiedAt(-1L)); - return new ArrayList<>(softwareModuleMetadataRepository.save(metadata)); + return Collections.unmodifiableList(softwareModuleMetadataRepository.save(metadata)); } @Override @@ -623,8 +620,8 @@ public class JpaSoftwareManagement implements SoftwareManagement { @Override public List findSoftwareModuleMetadataBySoftwareModuleId(final Long softwareModuleId) { - return new ArrayList<>( - softwareModuleMetadataRepository + return Collections + .unmodifiableList(softwareModuleMetadataRepository .findAll((Specification) (root, query, cb) -> cb.and(cb.equal( root.get(JpaSoftwareModuleMetadata_.softwareModule).get(JpaSoftwareModule_.id), diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java index 3b7f77b47..ca7eb4b3c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java @@ -299,4 +299,9 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst Constants.DST_DEFAULT_OS_WITH_APPS_NAME, "Default type with Firmware/OS and optional app(s).") .addMandatoryModuleType(os).addOptionalModuleType(app)); } + + @Override + public TenantMetaData getTenantMetadata(final Long tenantId) { + return tenantMetaDataRepository.findOne(tenantId); + } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java index e881d8ef8..f81415722 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTagManagement.java @@ -10,8 +10,8 @@ package org.eclipse.hawkbit.repository.jpa; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -111,7 +111,7 @@ public class JpaTagManagement implements TagManagement { } }); - final List save = new ArrayList<>(targetTagRepository.save(targetTags)); + final List save = Collections.unmodifiableList(targetTagRepository.save(targetTags)); afterCommit .afterCommit(() -> eventBus.post(new TargetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save))); return save; @@ -125,7 +125,7 @@ public class JpaTagManagement implements TagManagement { final List changed = new LinkedList<>(); for (final JpaTarget target : targetRepository.findByTag(tag)) { - target.getTags().remove(tag); + target.removeTag(tag); changed.add(target); } @@ -141,7 +141,7 @@ public class JpaTagManagement implements TagManagement { @Override public List findAllTargetTags() { - return new ArrayList<>(targetTagRepository.findAll()); + return Collections.unmodifiableList(targetTagRepository.findAll()); } @Override @@ -152,12 +152,12 @@ public class JpaTagManagement implements TagManagement { } private static Page convertTPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Page convertDsPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @Override @@ -213,7 +213,8 @@ public class JpaTagManagement implements TagManagement { throw new EntityAlreadyExistsException(); } } - final List save = new ArrayList<>(distributionSetTagRepository.save(distributionSetTags)); + final List save = Collections + .unmodifiableList(distributionSetTagRepository.save(distributionSetTags)); afterCommit.afterCommit( () -> eventBus.post(new DistributionSetTagCreatedBulkEvent(tenantAware.getCurrentTenant(), save))); @@ -228,7 +229,7 @@ public class JpaTagManagement implements TagManagement { final List changed = new LinkedList<>(); for (final JpaDistributionSet set : distributionSetRepository.findByTag(tag)) { - set.getTags().remove(tag); + set.removeTag(tag); changed.add(set); } @@ -254,7 +255,7 @@ public class JpaTagManagement implements TagManagement { @Override public List findAllDistributionSetTags() { - return new ArrayList<>(distributionSetTagRepository.findAll()); + return Collections.unmodifiableList(distributionSetTagRepository.findAll()); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java index 225c0b4b2..b9085c601 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -127,7 +128,7 @@ public class JpaTargetManagement implements TargetManagement { @Override public List findTargetByControllerID(final Collection controllerIDs) { - return new ArrayList<>(targetRepository + return Collections.unmodifiableList(targetRepository .findAll(TargetSpecifications.byControllerIdWithStatusAndAssignedInJoin(controllerIDs))); } @@ -193,7 +194,7 @@ public class JpaTargetManagement implements TargetManagement { toUpdate.forEach(target -> target.setNew(false)); - return new ArrayList<>(targetRepository.save(toUpdate)); + return Collections.unmodifiableList(targetRepository.save(toUpdate)); } @Override @@ -236,11 +237,11 @@ public class JpaTargetManagement implements TargetManagement { } private static Page convertPage(final Page findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements()); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } private static Slice convertPage(final Slice findAll, final Pageable pageable) { - return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, 0); + return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, 0); } @Override @@ -289,7 +290,7 @@ public class JpaTargetManagement implements TargetManagement { private static List> buildSpecificationList(final Collection status, final String searchText, final Long installedOrAssignedDistributionSetId, final Boolean selectTargetWithNoTag, final boolean fetch, final String... tagNames) { - final List> specList = new ArrayList<>(); + final List> specList = new LinkedList<>(); if (status != null && !status.isEmpty()) { specList.add(TargetSpecifications.hasTargetUpdateStatus(status, fetch)); } @@ -342,7 +343,7 @@ public class JpaTargetManagement implements TargetManagement { // all are already assigned -> unassign if (alreadyAssignedTargets.size() == allTargets.size()) { - alreadyAssignedTargets.forEach(target -> target.getTags().remove(tag)); + alreadyAssignedTargets.forEach(target -> target.removeTag(tag)); final TargetTagAssignmentResult result = new TargetTagAssignmentResult(0, 0, alreadyAssignedTargets.size(), Collections.emptyList(), alreadyAssignedTargets, tag); @@ -352,9 +353,10 @@ public class JpaTargetManagement implements TargetManagement { allTargets.removeAll(alreadyAssignedTargets); // some or none are assigned -> assign - allTargets.forEach(target -> target.getTags().add(tag)); + allTargets.forEach(target -> target.addTag(tag)); final TargetTagAssignmentResult result = new TargetTagAssignmentResult(alreadyAssignedTargets.size(), - allTargets.size(), 0, new ArrayList<>(targetRepository.save(allTargets)), Collections.emptyList(), tag); + allTargets.size(), 0, Collections.unmodifiableList(targetRepository.save(allTargets)), + Collections.emptyList(), tag); afterCommit.afterCommit(() -> eventBus.post(new TargetTagAssigmentResultEvent(result))); @@ -370,8 +372,8 @@ public class JpaTargetManagement implements TargetManagement { final List allTargets = targetRepository .findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(targetIds)); - allTargets.forEach(target -> target.getTags().add(tag)); - final List save = new ArrayList<>(targetRepository.save(allTargets)); + allTargets.forEach(target -> target.addTag(tag)); + final List save = Collections.unmodifiableList(targetRepository.save(allTargets)); afterCommit.afterCommit(() -> { final TargetTagAssignmentResult assigmentResult = new TargetTagAssignmentResult(0, save.size(), 0, save, @@ -383,11 +385,12 @@ public class JpaTargetManagement implements TargetManagement { } private List unAssignTag(final Collection targets, final TargetTag tag) { + @SuppressWarnings({ "unchecked", "rawtypes" }) final Collection toUnassign = (Collection) targets; - toUnassign.forEach(target -> target.getTags().remove(tag)); + toUnassign.forEach(target -> target.removeTag(tag)); - final List save = new ArrayList<>(targetRepository.save(toUnassign)); + final List save = Collections.unmodifiableList(targetRepository.save(toUnassign)); afterCommit.afterCommit(() -> { final TargetTagAssignmentResult assigmentResult = new TargetTagAssignmentResult(0, 0, save.size(), Collections.emptyList(), save, tag); @@ -407,7 +410,7 @@ public class JpaTargetManagement implements TargetManagement { @Modifying @Transactional(isolation = Isolation.READ_UNCOMMITTED) public Target unAssignTag(final String controllerID, final TargetTag targetTag) { - final List allTargets = new ArrayList<>(targetRepository + final List allTargets = Collections.unmodifiableList(targetRepository .findAll(TargetSpecifications.byControllerIdWithStatusAndTagsInJoin(Arrays.asList(controllerID)))); final List unAssignTag = unAssignTag(allTargets, targetTag); return unAssignTag.isEmpty() ? null : unAssignTag.get(0); @@ -463,7 +466,7 @@ public class JpaTargetManagement implements TargetManagement { final List resultList = entityManager.createQuery(query).setFirstResult(pageable.getOffset()) .setMaxResults(pageSize + 1).getResultList(); final boolean hasNext = resultList.size() > pageSize; - return new SliceImpl<>(new ArrayList<>(resultList), pageable, hasNext); + return new SliceImpl<>(Collections.unmodifiableList(resultList), pageable, hasNext); } private static Predicate[] specificationsToPredicate(final List> specifications, @@ -545,11 +548,8 @@ public class JpaTargetManagement implements TargetManagement { targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name), targetRoot.get(sortProperty)); final Specification spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class); - final List> specList = new ArrayList<>(); - specList.add(spec); - - final Predicate[] specificationsForMultiSelect = specificationsToPredicate(specList, targetRoot, multiselect, - cb); + final Predicate[] specificationsForMultiSelect = specificationsToPredicate(Lists.newArrayList(spec), targetRoot, + multiselect, cb); // if we have some predicates then add it to the where clause of the // multiselect @@ -611,18 +611,16 @@ public class JpaTargetManagement implements TargetManagement { targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) { throw new EntityAlreadyExistsException(); } - final List savedTargets = new ArrayList<>(); - for (final Target t : targets) { - final Target myTarget = createTarget(t, TargetUpdateStatus.UNKNOWN, null, t.getTargetInfo().getAddress()); - savedTargets.add(myTarget); - } - return savedTargets; + + return targets.stream() + .map(t -> createTarget(t, TargetUpdateStatus.UNKNOWN, null, t.getTargetInfo().getAddress())) + .collect(Collectors.toList()); } @Override public List findTargetsByTag(final String tagName) { final JpaTargetTag tag = targetTagRepository.findByNameEquals(tagName); - return new ArrayList<>(targetRepository.findByTag(tag)); + return Collections.unmodifiableList(targetRepository.findByTag(tag)); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java index c18f614fd..5dfb70633 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/aspects/ExceptionMappingAspectHandler.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.repository.jpa.aspects; import java.sql.SQLException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -36,6 +35,8 @@ import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.security.access.AccessDeniedException; import org.springframework.transaction.TransactionSystemException; +import com.google.common.collect.Maps; + /** * {@link Aspect} catches persistence exceptions and wraps them to custom * specific exceptions Additionally it checks and prevents access to certain @@ -49,14 +50,14 @@ public class ExceptionMappingAspectHandler implements Ordered { private static final Logger LOG = LoggerFactory.getLogger(ExceptionMappingAspectHandler.class); - private static final Map EXCEPTION_MAPPING = new HashMap<>(); + private static final Map EXCEPTION_MAPPING = Maps.newHashMapWithExpectedSize(4); /** * this is required to enable a certain order of exception and to select the * most specific mappable exception according to the type hierarchy of the * exception. */ - private static final List> MAPPED_EXCEPTION_ORDER = new ArrayList<>(); + private static final List> MAPPED_EXCEPTION_ORDER = new ArrayList<>(4); @Autowired private JpaVendorAdapter jpaVendorAdapter; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaArtifact.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaArtifact.java index 6ead1df9e..1cf6921a4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaArtifact.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaArtifact.java @@ -32,7 +32,7 @@ public abstract class AbstractJpaArtifact extends AbstractJpaTenantAwareBaseEnti private String md5Hash; @Column(name = "file_size") - private Long size; + private long size; @Override public abstract SoftwareModule getSoftwareModule(); @@ -56,11 +56,11 @@ public abstract class AbstractJpaArtifact extends AbstractJpaTenantAwareBaseEnti } @Override - public Long getSize() { + public long getSize() { return size; } - public void setSize(final Long size) { + public void setSize(final long size) { this.size = size; } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java index ef1868c3f..983cb2763 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java @@ -48,7 +48,7 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { @Version @Column(name = "optlock_revision") - private long optLockRevision; + private int optLockRevision; /** * Default constructor needed for JPA entities. @@ -106,11 +106,11 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { } @Override - public long getOptLockRevision() { + public int getOptLockRevision() { return optLockRevision; } - public void setOptLockRevision(final long optLockRevision) { + public void setOptLockRevision(final int optLockRevision) { this.optLockRevision = optLockRevision; } @@ -142,7 +142,7 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { final int prime = 31; int result = 1; result = prime * result + (id == null ? 0 : id.hashCode()); - result = prime * result + (int) (optLockRevision ^ optLockRevision >>> 32); + result = prime * result + optLockRevision; result = prime * result + this.getClass().getName().hashCode(); return result; } @@ -179,5 +179,4 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { return true; } - } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaMetaData.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaMetaData.java index a9f93cb86..c6df089f7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaMetaData.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaMetaData.java @@ -26,7 +26,7 @@ public abstract class AbstractJpaMetaData implements MetaData { private static final long serialVersionUID = 1L; @Id - @Column(name = "meta_key", length = 128) + @Column(name = "meta_key", nullable = false, length = 128) @Size(min = 1, max = 128) @NotNull private String key; @@ -37,7 +37,6 @@ public abstract class AbstractJpaMetaData implements MetaData { private String value; public AbstractJpaMetaData(final String key, final String value) { - super(); this.key = key; this.value = value; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java index 0011cc2da..634d4490f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedEntity.java @@ -40,7 +40,7 @@ public abstract class AbstractJpaNamedEntity extends AbstractJpaTenantAwareBaseE * Default constructor. */ public AbstractJpaNamedEntity() { - super(); + // Default constructor needed for JPA entities } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedVersionedEntity.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedVersionedEntity.java index 9c78f976c..9e45f3a76 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedVersionedEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaNamedVersionedEntity.java @@ -47,7 +47,7 @@ public abstract class AbstractJpaNamedVersionedEntity extends AbstractJpaNamedEn } AbstractJpaNamedVersionedEntity() { - super(); + // Default constructor needed for JPA entities } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTag.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTag.java index 12aad8d6d..f759d16fc 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTag.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaTag.java @@ -31,7 +31,7 @@ public abstract class AbstractJpaTag extends AbstractJpaNamedEntity implements T private String colour; protected AbstractJpaTag() { - super(); + // Default constructor needed for JPA entities } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java index 4b5b6567e..ee3e17b8b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.List; import javax.persistence.CascadeType; @@ -134,7 +135,11 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio @Override public List getActionStatus() { - return actionStatus; + if (actionStatus == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(actionStatus); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java index 102b25952..8fa774c00 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionStatus.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.repository.jpa.model; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.persistence.CollectionTable; @@ -48,6 +49,8 @@ import com.google.common.base.Splitter; // sub entities @SuppressWarnings("squid:S2160") public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements ActionStatus { + private static final int MESSAGE_ENTRY_LENGTH = 512; + private static final long serialVersionUID = 1L; @Column(name = "target_occurred_at") @@ -67,14 +70,14 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements @CollectionTable(name = "sp_action_status_messages", joinColumns = @JoinColumn(name = "action_status_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_stat_msg_act_stat")), indexes = { @Index(name = "sp_idx_action_status_msgs_01", columnList = "action_status_id") }) @Column(name = "detail_message", length = 512) - private final List messages = new ArrayList<>(); + private List messages; /** * Note: filled only in {@link Status#DOWNLOAD}. */ @Transient @CacheField(key = CacheKeys.DOWNLOAD_PROGRESS_PERCENT) - private int downloadProgressPercent; + private short downloadProgressPercent; /** * Creates a new {@link ActionStatus} object. @@ -86,7 +89,7 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements * @param occurredAt * the occurred timestamp */ - public JpaActionStatus(final Action action, final Status status, final Long occurredAt) { + public JpaActionStatus(final Action action, final Status status, final long occurredAt) { this.action = (JpaAction) action; this.status = status; this.occurredAt = occurredAt; @@ -119,7 +122,7 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements } @Override - public int getDownloadProgressPercent() { + public short getDownloadProgressPercent() { return downloadProgressPercent; } @@ -136,13 +139,20 @@ public class JpaActionStatus extends AbstractJpaTenantAwareBaseEntity implements @Override public final void addMessage(final String message) { if (message != null) { - Splitter.fixedLength(512).split(message).forEach(messages::add); + if (messages == null) { + messages = new ArrayList<>((message.length() / MESSAGE_ENTRY_LENGTH) + 1); + } + Splitter.fixedLength(MESSAGE_ENTRY_LENGTH).split(message).forEach(messages::add); } } @Override public List getMessages() { - return messages; + if (messages == null) { + messages = Collections.emptyList(); + } + + return Collections.unmodifiableList(messages); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionWithStatusCount.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionWithStatusCount.java index 32b01cbe2..648ba344b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionWithStatusCount.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaActionWithStatusCount.java @@ -59,7 +59,7 @@ public class JpaActionWithStatusCount implements ActionWithStatusCount { // Exception squid:S00107 - needed this way for JPA to fill the view @SuppressWarnings("squid:S00107") public JpaActionWithStatusCount(final Long actionId, final ActionType actionType, final boolean active, - final long forcedTime, final Status status, final Long actionCreatedAt, final Long actionLastModifiedAt, + final Long forcedTime, final Status status, final Long actionCreatedAt, final Long actionLastModifiedAt, final Long dsId, final String dsName, final String dsVersion, final Long actionStatusCount, final String rolloutName) { this.dsId = dsId; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java index 9fc27f1e8..9e6efe826 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.repository.jpa.model; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -82,13 +81,13 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @JoinTable(name = "sp_ds_module", joinColumns = { @JoinColumn(name = "ds_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_ds")) }, inverseJoinColumns = { @JoinColumn(name = "module_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_module_module")) }) - private final Set modules = new HashSet<>(); + private Set modules; @ManyToMany(targetEntity = JpaDistributionSetTag.class) @JoinTable(name = "sp_ds_dstag", joinColumns = { @JoinColumn(name = "ds", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_ds")) }, inverseJoinColumns = { @JoinColumn(name = "TAG", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstag_tag")) }) - private Set tags = new HashSet<>(); + private Set tags; @Column(name = "deleted") private boolean deleted; @@ -106,7 +105,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @OneToMany(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetMetadata.class, cascade = { CascadeType.REMOVE }) @JoinColumn(name = "ds_id", insertable = false, updatable = false) - private final List metadata = new ArrayList<>(); + private List metadata; @ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaDistributionSetType.class) @JoinColumn(name = "ds_id", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_dstype_ds")) @@ -120,7 +119,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen * Default constructor. */ public JpaDistributionSet() { - super(); + // Default constructor for JPA } /** @@ -152,7 +151,29 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @Override public Set getTags() { - return tags; + if (tags == null) { + return Collections.emptySet(); + } + + return Collections.unmodifiableSet(tags); + } + + @Override + public boolean addTag(final DistributionSetTag tag) { + if (tags == null) { + tags = new HashSet<>(); + } + + return tags.add(tag); + } + + @Override + public boolean removeTag(final DistributionSetTag tag) { + if (tags == null) { + return false; + } + + return tags.remove(tag); } @Override @@ -162,11 +183,19 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @Override public List getMetadata() { + if (metadata == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(metadata); } public List getActions() { - return actions; + if (actions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(actions); } @Override @@ -186,19 +215,22 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen return this; } - public DistributionSet setTags(final Set tags) { - this.tags = tags; - return this; - } - @Override public List getAssignedTargets() { - return assignedToTargets; + if (assignedToTargets == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(assignedToTargets); } @Override public List getInstalledTargets() { - return installedAtTargets; + if (installedAtTargets == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(installedAtTargets); } @Override @@ -209,21 +241,20 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @Override public Set getModules() { + if (modules == null) { + return Collections.emptySet(); + } + return Collections.unmodifiableSet(modules); } @Override public boolean addModule(final SoftwareModule softwareModule) { - - // we cannot allow that modules are added without a type defined - if (type == null) { - throw new DistributionSetTypeUndefinedException(); + if (modules == null) { + modules = new HashSet<>(); } - // check if it is allowed to such a module to this DS type - if (!type.containsModuleType(softwareModule.getType())) { - throw new UnsupportedSoftwareModuleForThisDistributionSetException(); - } + checkTypeCompatability(softwareModule); final Optional found = modules.stream() .filter(module -> module.getId().equals(softwareModule.getId())).findFirst(); @@ -249,8 +280,24 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen return false; } + private void checkTypeCompatability(final SoftwareModule softwareModule) { + // we cannot allow that modules are added without a type defined + if (type == null) { + throw new DistributionSetTypeUndefinedException(); + } + + // check if it is allowed to such a module to this DS type + if (!type.containsModuleType(softwareModule.getType())) { + throw new UnsupportedSoftwareModuleForThisDistributionSetException(); + } + } + @Override public boolean removeModule(final SoftwareModule softwareModule) { + if (modules == null) { + return false; + } + final Optional found = modules.stream() .filter(module -> module.getId().equals(softwareModule.getId())).findFirst(); @@ -266,6 +313,10 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen @Override public SoftwareModule findFirstModuleByType(final SoftwareModuleType type) { + if (modules == null) { + return null; + } + final Optional result = modules.stream().filter(module -> module.getType().equals(type)) .findFirst(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java index 1663fb620..dee17a80e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetTag.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.List; import javax.persistence.Entity; @@ -68,26 +69,10 @@ public class JpaDistributionSetTag extends AbstractJpaTag implements Distributio @Override public List getAssignedToDistributionSet() { - return assignedToDistributionSet; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + this.getClass().getName().hashCode(); - return result; - } - - @Override - public boolean equals(final Object obj) { // NOSONAR - as this is generated - if (!super.equals(obj)) { - return false; - } - if (!(obj instanceof DistributionSetTag)) { - return false; + if (assignedToDistributionSet == null) { + return Collections.emptyList(); } - return true; + return Collections.unmodifiableList(assignedToDistributionSet); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java index 2a696aac9..44bed8cce 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSetType.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -24,6 +25,7 @@ import javax.persistence.Table; import javax.persistence.UniqueConstraint; import javax.validation.constraints.Size; +import org.apache.commons.collections4.CollectionUtils; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; @@ -48,7 +50,7 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di @OneToMany(targetEntity = DistributionSetTypeElement.class, cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true) @JoinColumn(name = "distribution_set_type", insertable = false, updatable = false) - private final Set elements = new HashSet<>(); + private Set elements; @Column(name = "type_key", nullable = false, length = 64) @Size(max = 64) @@ -108,23 +110,52 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di @Override public Set getMandatoryModuleTypes() { + if (elements == null) { + return Collections.emptySet(); + } + return elements.stream().filter(element -> element.isMandatory()).map(element -> element.getSmType()) .collect(Collectors.toSet()); } @Override public Set getOptionalModuleTypes() { + if (elements == null) { + return Collections.emptySet(); + } + return elements.stream().filter(element -> !element.isMandatory()).map(element -> element.getSmType()) .collect(Collectors.toSet()); } @Override public boolean areModuleEntriesIdentical(final DistributionSetType dsType) { + if (!(dsType instanceof JpaDistributionSetType) || isOneModuleListEmpty(dsType)) { + return false; + } else if (areBothModuleListsEmpty(dsType)) { + return true; + } + return new HashSet(((JpaDistributionSetType) dsType).elements).equals(elements); } + private boolean isOneModuleListEmpty(final DistributionSetType dsType) { + return (!CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements) + && CollectionUtils.isEmpty(elements)) + || (CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements) + && !CollectionUtils.isEmpty(elements)); + } + + private boolean areBothModuleListsEmpty(final DistributionSetType dsType) { + return CollectionUtils.isEmpty(((JpaDistributionSetType) dsType).elements) && CollectionUtils.isEmpty(elements); + } + @Override public DistributionSetType addOptionalModuleType(final SoftwareModuleType smType) { + if (elements == null) { + elements = new HashSet<>(); + } + elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, false)); return this; @@ -132,6 +163,10 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di @Override public DistributionSetType addMandatoryModuleType(final SoftwareModuleType smType) { + if (elements == null) { + elements = new HashSet<>(); + } + elements.add(new DistributionSetTypeElement(this, (JpaSoftwareModuleType) smType, true)); return this; @@ -139,6 +174,10 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di @Override public DistributionSetType removeModuleType(final Long smTypeId) { + if (elements == null) { + return this; + } + // we search by id (standard equals compares also revison) final Optional found = elements.stream() .filter(element -> element.getSmType().getId().equals(smTypeId)).findFirst(); @@ -177,7 +216,11 @@ public class JpaDistributionSetType extends AbstractJpaNamedEntity implements Di } public Set getElements() { - return elements; + if (elements == null) { + return Collections.emptySet(); + } + + return Collections.unmodifiableSet(elements); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java index 6d720a75d..dc1b82ac2 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRollout.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.List; import javax.persistence.Column; @@ -108,11 +109,11 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event @Override public List getRolloutGroups() { - return rolloutGroups; - } + if (rolloutGroups == null) { + return Collections.emptyList(); + } - public void setRolloutGroups(final List rolloutGroups) { - this.rolloutGroups = rolloutGroups; + return Collections.unmodifiableList(rolloutGroups); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRolloutGroup.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRolloutGroup.java index 750e61185..2c17a41c7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRolloutGroup.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaRolloutGroup.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.persistence.CascadeType; @@ -58,7 +58,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr @OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST }, targetEntity = RolloutTargetGroup.class) @JoinColumn(name = "rolloutGroup_Id", insertable = false, updatable = false) - private final List rolloutTargetGroup = new ArrayList<>(); + private List rolloutTargetGroup; @ManyToOne(fetch = FetchType.LAZY) private JpaRolloutGroup parent; @@ -92,7 +92,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr private String errorActionExp; @Column(name = "total_targets") - private long totalTargets; + private int totalTargets; @Transient private transient TotalTargetCountStatus totalTargetCountStatus; @@ -118,7 +118,11 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr } public List getRolloutTargetGroup() { - return rolloutTargetGroup; + if (rolloutTargetGroup == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(rolloutTargetGroup); } @Override @@ -201,11 +205,11 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr } @Override - public long getTotalTargets() { + public int getTotalTargets() { return totalTargets; } - public void setTotalTargets(final long totalTargets) { + public void setTotalTargets(final int totalTargets) { this.totalTargets = totalTargets; } @@ -223,7 +227,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr @Override public TotalTargetCountStatus getTotalTargetCountStatus() { if (totalTargetCountStatus == null) { - totalTargetCountStatus = new TotalTargetCountStatus(totalTargets); + totalTargetCountStatus = new TotalTargetCountStatus(new Long(totalTargets)); } return totalTargetCountStatus; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java index e52857c37..86b64fd31 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java @@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.model; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Optional; @@ -40,6 +41,8 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.persistence.annotations.CascadeOnDelete; +import com.google.common.collect.Lists; + /** * Base Software Module that is supported by OS level provisioning mechanism on * the edge controller, e.g. OS, JVM, AgentHub. @@ -64,7 +67,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement private JpaSoftwareModuleType type; @ManyToMany(mappedBy = "modules", targetEntity = JpaDistributionSet.class, fetch = FetchType.LAZY) - private final List assignedTo = new ArrayList<>(); + private List assignedTo; @Column(name = "deleted") private boolean deleted; @@ -82,13 +85,13 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement @CascadeOnDelete @OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaSoftwareModuleMetadata.class) @JoinColumn(name = "sw_id", insertable = false, updatable = false) - private final List metadata = new ArrayList<>(); + private List metadata; /** * Default constructor. */ public JpaSoftwareModule() { - super(); + // Default constructor for JPA } /** @@ -120,6 +123,8 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement public void addArtifact(final LocalArtifact artifact) { if (null == artifacts) { artifacts = new ArrayList<>(4); + artifacts.add(artifact); + return; } if (!artifacts.contains(artifact)) { @@ -134,7 +139,9 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement @Override public void addArtifact(final ExternalArtifact artifact) { if (null == externalArtifacts) { - externalArtifacts = new ArrayList<>(4); + externalArtifacts = new LinkedList<>(); + externalArtifacts.add(artifact); + return; } if (!externalArtifacts.contains(artifact)) { @@ -143,28 +150,18 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement } - /** - * @param artifactId - * to look for - * @return found {@link Artifact} - */ @Override public Optional getLocalArtifact(final Long artifactId) { - if (null == artifacts) { + if (artifacts == null) { return Optional.empty(); } return artifacts.stream().filter(artifact -> artifact.getId().equals(artifactId)).findFirst(); } - /** - * @param fileName - * to look for - * @return found {@link Artifact} - */ @Override public Optional getLocalArtifactByFilename(final String fileName) { - if (null == artifacts) { + if (artifacts == null) { return Optional.empty(); } @@ -177,11 +174,18 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement */ @Override public List getArtifacts() { - final List result = new ArrayList<>(); - result.addAll(artifacts); + if (artifacts == null && externalArtifacts == null) { + return Collections.emptyList(); + } else if (artifacts == null) { + return Collections.unmodifiableList(externalArtifacts); + } else if (externalArtifacts == null) { + return Collections.unmodifiableList(artifacts); + } + + final List result = Lists.newLinkedList(artifacts); result.addAll(externalArtifacts); - return result; + return Collections.unmodifiableList(result); } /** @@ -207,7 +211,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement */ @Override public void removeArtifact(final LocalArtifact artifact) { - if (null != artifacts) { + if (artifacts != null) { artifacts.remove(artifact); } } @@ -218,7 +222,7 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement */ @Override public void removeArtifact(final ExternalArtifact artifact) { - if (null != externalArtifacts) { + if (externalArtifacts != null) { externalArtifacts.remove(artifact); } } @@ -248,11 +252,12 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement this.type = (JpaSoftwareModuleType) type; } - /** - * @return immutable list of meta data elements. - */ @Override public List getMetadata() { + if (metadata == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(metadata); } @@ -262,12 +267,13 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement + ", revision=" + getOptLockRevision() + ", Id=" + getId() + ", type=" + getType().getName() + "]"; } - /** - * @return the assignedTo - */ @Override public List getAssignedTo() { - return assignedTo; + if (assignedTo == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(assignedTo); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java index 476dea18b..191238c88 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTarget.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.repository.jpa.model; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -85,13 +86,13 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable tags = new HashSet<>(); + private Set tags; @CascadeOnDelete @OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = { CascadeType.REMOVE }, targetEntity = JpaAction.class) @JoinColumn(name = "target", insertable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_targ_act_hist_targ")) - private final List actions = new ArrayList<>(); + private List actions; @ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class) @JoinColumn(name = "assigned_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_assign_ds")) @@ -114,7 +115,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable rolloutTargetGroup = new ArrayList<>(); + private List rolloutTargetGroup; /** * Constructor. @@ -141,12 +142,8 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable getTags() { - return tags; + if (tags == null) { + return Collections.emptySet(); + } + + return Collections.unmodifiableSet(tags); + } + + public List getRolloutTargetGroup() { + if (rolloutTargetGroup == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(rolloutTargetGroup); + } + + @Override + public boolean addTag(final TargetTag tag) { + if (tags == null) { + tags = new HashSet<>(); + } + + return tags.add(tag); + } + + @Override + public boolean removeTag(final TargetTag tag) { + if (tags == null) { + return false; + } + + return tags.remove(tag); } public void setAssignedDistributionSet(final DistributionSet assignedDistributionSet) { @@ -172,13 +199,21 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable tags) { - this.tags = tags; - } - @Override public List getActions() { - return actions; + if (actions == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(actions); + } + + public boolean addAction(final Action action) { + if (actions == null) { + actions = new ArrayList<>(); + } + + return actions.add(action); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetInfo.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetInfo.java index ea3713fa3..6dbe2fcd7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetInfo.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetInfo.java @@ -220,7 +220,7 @@ public class JpaTargetInfo implements Persistable, TargetInfo, EventAwareE return lastTargetQuery; } - public void setLastTargetQuery(final Long lastTargetQuery) { + public void setLastTargetQuery(final long lastTargetQuery) { this.lastTargetQuery = lastTargetQuery; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java index 96989f14a..a77d4b42d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaTargetTag.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.model; +import java.util.Collections; import java.util.List; import javax.persistence.Entity; @@ -65,27 +66,11 @@ public class JpaTargetTag extends AbstractJpaTag implements TargetTag { @Override public List getAssignedToTargets() { - return assignedToTargets; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + this.getClass().getName().hashCode(); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (!super.equals(obj)) { - return false; - } - if (!(obj instanceof TargetTag)) { - return false; + if (assignedToTargets == null) { + return Collections.emptyList(); } - return true; + return Collections.unmodifiableList(assignedToTargets); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java index 3e098c701..676cd9ad1 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java @@ -29,8 +29,8 @@ public class ThresholdRolloutGroupSuccessCondition implements RolloutGroupCondit @Override public boolean eval(final Rollout rollout, final RolloutGroup rolloutGroup, final String expression) { - final Long totalGroup = rolloutGroup.getTotalTargets(); - final Long finished = this.actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(), + final long totalGroup = rolloutGroup.getTotalTargets(); + final long finished = this.actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(), rolloutGroup.getId(), Action.Status.FINISHED); try { final Integer threshold = Integer.valueOf(expression); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java index ac12ff4f9..cb117ad7a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java @@ -279,23 +279,23 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoD /** * Test method for - * {@link org.eclipse.hawkbit.repository.ArtifactManagement#findArtifact(java.lang.Long)} + * {@link org.eclipse.hawkbit.repository.ArtifactManagement#findLocalArtifact(java.lang.Long)} * . * * @throws IOException * @throws NoSuchAlgorithmException */ @Test - @Description("Loads an artifact based on given ID.") - public void findArtifact() throws NoSuchAlgorithmException, IOException { + @Description("Loads an local artifact based on given ID.") + public void findLocalArtifact() throws NoSuchAlgorithmException, IOException { SoftwareModule sm = new JpaSoftwareModule(softwareManagement.findSoftwareModuleTypeByKey("os"), "name 1", "version 1", null, null); sm = softwareManagement.createSoftwareModule(sm); - final Artifact result = artifactManagement.createLocalArtifact(new RandomGeneratedInputStream(5 * 1024), + final LocalArtifact result = artifactManagement.createLocalArtifact(new RandomGeneratedInputStream(5 * 1024), sm.getId(), "file1", false); - assertThat(artifactManagement.findArtifact(result.getId())).isEqualTo(result); + assertThat(artifactManagement.findLocalArtifact(result.getId())).isEqualTo(result); } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index 565513107..1229da2cd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -106,7 +106,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { assertThat(findActionsWithStatusCountByTarget).as("wrong action size").hasSize(1); assertThat(findActionsWithStatusCountByTarget.get(0).getActionStatusCount()).as("wrong action status size") - .isEqualTo(3L); + .isEqualTo(3); } @Test @@ -842,8 +842,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { final DistributionSet ds = testdataFactory.createDistributionSet("a"); // assign ds to create an action final DistributionSetAssignmentResult assignDistributionSet = deploymentManagement.assignDistributionSet( - ds.getId(), ActionType.SOFT, org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, - target.getControllerId()); + ds.getId(), ActionType.SOFT, + org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, target.getControllerId()); final Action action = deploymentManagement.findActionWithDetails(assignDistributionSet.getActions().get(0)); // verify preparation Action findAction = deploymentManagement.findAction(action.getId()); @@ -865,8 +865,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { final DistributionSet ds = testdataFactory.createDistributionSet("a"); // assign ds to create an action final DistributionSetAssignmentResult assignDistributionSet = deploymentManagement.assignDistributionSet( - ds.getId(), ActionType.FORCED, org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, - target.getControllerId()); + ds.getId(), ActionType.FORCED, + org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, target.getControllerId()); final Action action = deploymentManagement.findActionWithDetails(assignDistributionSet.getActions().get(0)); // verify perparation Action findAction = deploymentManagement.findAction(action.getId()); @@ -937,7 +937,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { for (final Target myt : targets) { boolean found = false; for (final TargetAssignDistributionSetEvent event : events) { - if (event.getControllerId().equals(myt.getControllerId())) { + if (event.getTarget().getControllerId().equals(myt.getControllerId())) { found = true; final List activeActionsByTarget = deploymentManagement.findActiveActionsByTarget(myt); assertThat(activeActionsByTarget).as("size of active actions for target is wrong").isNotEmpty(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetManagementTest.java index b5ba12946..bcb6a1f32 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetManagementTest.java @@ -396,14 +396,14 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest { // create a DS final DistributionSet ds = testdataFactory.createDistributionSet("testDs"); // initial opt lock revision must be zero - assertThat(ds.getOptLockRevision()).isEqualTo(1L); + assertThat(ds.getOptLockRevision()).isEqualTo(1); // create an DS meta data entry final DistributionSetMetadata dsMetadata = distributionSetManagement .createDistributionSetMetadata(new JpaDistributionSetMetadata(knownKey, ds, knownValue)); DistributionSet changedLockRevisionDS = distributionSetManagement.findDistributionSetById(ds.getId()); - assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(2L); + assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(2); // modifying the meta data value dsMetadata.setValue(knownUpdateValue); @@ -419,7 +419,7 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest { // module so opt lock // revision must be three changedLockRevisionDS = distributionSetManagement.findDistributionSetById(ds.getId()); - assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(3L); + assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(3); assertThat(changedLockRevisionDS.getLastModifiedAt()).isGreaterThan(0L); // verify updated meta data contains the updated value diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java index 739cf7f64..4f44d260c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/SoftwareManagementTest.java @@ -848,7 +848,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD final SoftwareModule ah = softwareManagement .createSoftwareModule(new JpaSoftwareModule(appType, "agent-hub", "1.0.1", null, "")); - assertThat(ah.getOptLockRevision()).isEqualTo(1L); + assertThat(ah.getOptLockRevision()).isEqualTo(1); final SoftwareModuleMetadata swMetadata1 = new JpaSoftwareModuleMetadata(knownKey1, ah, knownValue1); @@ -858,7 +858,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD .createSoftwareModuleMetadata(Lists.newArrayList(swMetadata1, swMetadata2)); final SoftwareModule changedLockRevisionModule = softwareManagement.findSoftwareModuleById(ah.getId()); - assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2L); + assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2); assertThat(softwareModuleMetadata).hasSize(2); assertThat(softwareModuleMetadata.get(0)).isNotNull(); @@ -900,7 +900,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD final SoftwareModule ah = softwareManagement .createSoftwareModule(new JpaSoftwareModule(appType, "agent-hub", "1.0.1", null, "")); // initial opt lock revision must be 1 - assertThat(ah.getOptLockRevision()).isEqualTo(1L); + assertThat(ah.getOptLockRevision()).isEqualTo(1); // create an software module meta data entry final List softwareModuleMetadata = softwareManagement.createSoftwareModuleMetadata( @@ -910,7 +910,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD // because we are modifying the // base software module SoftwareModule changedLockRevisionModule = softwareManagement.findSoftwareModuleById(ah.getId()); - assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2L); + assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(2); // modifying the meta data value softwareModuleMetadata.get(0).setValue(knownUpdateValue); @@ -925,7 +925,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD // module so opt lock // revision must be two changedLockRevisionModule = softwareManagement.findSoftwareModuleById(ah.getId()); - assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(3L); + assertThat(changedLockRevisionModule.getOptLockRevision()).isEqualTo(3); // verify updated meta data contains the updated value assertThat(updated).isNotNull(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java index 1e8f102c7..35e326f4a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java @@ -686,7 +686,8 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { final DistributionSet ds = testdataFactory.createDistributionSet("a"); - targAssigned = deploymentManagement.assignDistributionSet(ds, targAssigned).getAssignedEntity(); + targAssigned = Lists + .newLinkedList(deploymentManagement.assignDistributionSet(ds, targAssigned).getAssignedEntity()); targInstalled = deploymentManagement.assignDistributionSet(ds, targInstalled).getAssignedEntity(); targInstalled = sendUpdateActionStatusToTargets(ds, targInstalled, Status.FINISHED, "installed"); @@ -756,7 +757,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { private List sendUpdateActionStatusToTargets(final DistributionSet dsA, final Iterable targs, final Status status, final String... msgs) { - final List result = new ArrayList(); + final List result = new ArrayList<>(); for (final Target t : targs) { final List findByTarget = actionRepository.findByTarget((JpaTarget) t); for (final Action action : findByTarget) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java index 06f853db6..4671f701d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java @@ -541,28 +541,30 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the assigment of tags to the a single target.") public void targetTagAssignment() { - Target t1 = testdataFactory.generateTarget("id-1", "blablub"); + final Target t1 = testdataFactory.generateTarget("id-1", "blablub"); final int noT2Tags = 4; final int noT1Tags = 3; final List t1Tags = tagManagement .createTargetTags(testdataFactory.generateTargetTags(noT1Tags, "tag1")); - t1.getTags().addAll(t1Tags); - t1 = targetManagement.createTarget(t1); - Target t2 = testdataFactory.generateTarget("id-2", "blablub"); + t1Tags.forEach(tag -> ((JpaTarget) t1).addTag(tag)); + + targetManagement.createTarget(t1); + final Target t2 = testdataFactory.generateTarget("id-2", "blablub"); final List t2Tags = tagManagement .createTargetTags(testdataFactory.generateTargetTags(noT2Tags, "tag2")); - t2.getTags().addAll(t2Tags); - t2 = targetManagement.createTarget(t2); - t1 = targetManagement.findTargetByControllerID(t1.getControllerId()); - assertThat(t1.getTags()).as("Tag size is wrong").hasSize(noT1Tags).containsAll(t1Tags); - assertThat(t1.getTags()).as("Tag size is wrong").hasSize(noT1Tags) + t2Tags.forEach(tag -> ((JpaTarget) t2).addTag(tag)); + targetManagement.createTarget(t2); + + final Target t11 = targetManagement.findTargetByControllerID(t1.getControllerId()); + assertThat(t11.getTags()).as("Tag size is wrong").hasSize(noT1Tags).containsAll(t1Tags); + assertThat(t11.getTags()).as("Tag size is wrong").hasSize(noT1Tags) .doesNotContain(Iterables.toArray(t2Tags, TargetTag.class)); - t2 = targetManagement.findTargetByControllerID(t2.getControllerId()); - assertThat(t2.getTags()).as("Tag size is wrong").hasSize(noT2Tags).containsAll(t2Tags); - assertThat(t2.getTags()).as("Tag size is wrong").hasSize(noT2Tags) + final Target t21 = targetManagement.findTargetByControllerID(t2.getControllerId()); + assertThat(t21.getTags()).as("Tag size is wrong").hasSize(noT2Tags).containsAll(t2Tags); + assertThat(t21.getTags()).as("Tag size is wrong").hasSize(noT2Tags) .doesNotContain(Iterables.toArray(t1Tags, TargetTag.class)); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java index 3138c9f86..884b02459 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java @@ -18,7 +18,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; -import org.eclipse.hawkbit.repository.model.Target; import org.junit.Before; import org.junit.Test; import org.springframework.data.domain.PageRequest; @@ -32,7 +31,7 @@ import ru.yandex.qatools.allure.annotations.Stories; @Stories("RSQL filter actions") public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest { - private Target target; + private JpaTarget target; private JpaAction action; @Before @@ -42,7 +41,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest { targetManagement.createTarget(target); action = new JpaAction(); action.setActionType(ActionType.SOFT); - target.getActions().add(action); + target.addAction(action); action.setTarget(target); actionRepository.save(action); for (int i = 0; i < 10; i++) { @@ -51,7 +50,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest { newAction.setActive(i % 2 == 0); newAction.setTarget(target); actionRepository.save(newAction); - target.getActions().add(newAction); + target.addAction(newAction); } } diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java index 499925540..d665565a6 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java @@ -11,6 +11,8 @@ package org.eclipse.hawkbit; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties; +import org.eclipse.hawkbit.api.PropertyBasedArtifactUrlHandler; import org.eclipse.hawkbit.cache.CacheConstants; import org.eclipse.hawkbit.cache.TenancyCacheManager; import org.eclipse.hawkbit.cache.TenantAwareCacheManager; @@ -49,7 +51,8 @@ import com.mongodb.MongoClientOptions; */ @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY, proxyTargetClass = false, securedEnabled = true) -@EnableConfigurationProperties({ HawkbitServerProperties.class, DdiSecurityProperties.class }) +@EnableConfigurationProperties({ HawkbitServerProperties.class, DdiSecurityProperties.class, + ArtifactUrlHandlerProperties.class }) @Profile("test") @EnableAutoConfiguration public class TestConfiguration implements AsyncConfigurer { @@ -63,6 +66,12 @@ public class TestConfiguration implements AsyncConfigurer { return new TestdataFactory(); } + @Bean + public PropertyBasedArtifactUrlHandler testPropertyBasedArtifactUrlHandler( + final ArtifactUrlHandlerProperties urlHandlerProperties) { + return new PropertyBasedArtifactUrlHandler(urlHandlerProperties); + } + @Bean public MongoClientOptions options() { return MongoClientOptions.builder().connectTimeout(500).maxWaitTime(500).connectionsPerHost(2) diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/TenantUserPasswordAuthenticationToken.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/TenantUserPasswordAuthenticationToken.java index 77beaa698..036d5a065 100644 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/TenantUserPasswordAuthenticationToken.java +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/TenantUserPasswordAuthenticationToken.java @@ -68,4 +68,35 @@ public class TenantUserPasswordAuthenticationToken extends UsernamePasswordAuthe public Object getTenant() { return tenant; } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((tenant == null) ? 0 : tenant.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final TenantUserPasswordAuthenticationToken other = (TenantUserPasswordAuthenticationToken) obj; + if (tenant == null) { + if (other.tenant != null) { + return false; + } + } else if (!tenant.equals(other.tenant)) { + return false; + } + return true; + } + } diff --git a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticateSecurityTokenFilter.java b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticateSecurityTokenFilter.java index 8ff1e9ebc..b953453ea 100644 --- a/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticateSecurityTokenFilter.java +++ b/hawkbit-security-integration/src/main/java/org/eclipse/hawkbit/security/ControllerPreAuthenticateSecurityTokenFilter.java @@ -8,21 +8,16 @@ */ package org.eclipse.hawkbit.security; +import java.util.Optional; + import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken; -import org.eclipse.hawkbit.im.authentication.SpPermission; -import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.context.SecurityContextImpl; /** * An pre-authenticated processing filter which extracts (if enabled through @@ -68,11 +63,12 @@ public class ControllerPreAuthenticateSecurityTokenFilter extends AbstractContro @Override public HeaderAuthentication getPreAuthenticatedPrincipal(final TenantSecurityToken secruityToken) { + final String controllerId = resolveControllerId(secruityToken); final String authHeader = secruityToken.getHeader(TenantSecurityToken.AUTHORIZATION_HEADER); if ((authHeader != null) && authHeader.startsWith(TARGET_SECURITY_TOKEN_AUTH_SCHEME)) { LOGGER.debug("found authorization header with scheme {} using target security token for authentication", TARGET_SECURITY_TOKEN_AUTH_SCHEME); - return new HeaderAuthentication(secruityToken.getControllerId(), authHeader.substring(OFFSET_TARGET_TOKEN)); + return new HeaderAuthentication(controllerId, authHeader.substring(OFFSET_TARGET_TOKEN)); } LOGGER.debug( "security token filter is enabled but requst does not contain either the necessary path variables {} or the authorization header with scheme {}", @@ -81,51 +77,36 @@ public class ControllerPreAuthenticateSecurityTokenFilter extends AbstractContro } @Override - public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken secruityToken) { - final String securityToken = tenantAware.runAsTenant(secruityToken.getTenant(), - new GetSecurityTokenTenantRunner(secruityToken.getTenant(), secruityToken.getControllerId())); - return new HeaderAuthentication(secruityToken.getControllerId(), securityToken); + public HeaderAuthentication getPreAuthenticatedCredentials(final TenantSecurityToken securityToken) { + final Target target = systemSecurityContext.runAsSystemAsTenant(() -> { + if (securityToken.getTargetId() != null) { + return controllerManagement.findByTargetId(securityToken.getTargetId()); + } + return controllerManagement.findByControllerId(securityToken.getControllerId()); + }, securityToken.getTenant()); + + if (target == null) { + return null; + } + final String targetSecurityToken = systemSecurityContext.runAsSystemAsTenant(() -> target.getSecurityToken(), + securityToken.getTenant()); + return new HeaderAuthentication(target.getControllerId(), targetSecurityToken); + } + + private String resolveControllerId(final TenantSecurityToken securityToken) { + if (securityToken.getControllerId() != null) { + return securityToken.getControllerId(); + } + final Optional foundTarget = Optional.ofNullable(systemSecurityContext.runAsSystemAsTenant( + () -> controllerManagement.findByTargetId(securityToken.getTargetId()), securityToken.getTenant())); + if (!foundTarget.isPresent()) { + return null; + } + return foundTarget.get().getControllerId(); } @Override protected TenantConfigurationKey getTenantConfigurationKey() { return TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED; } - - private final class GetSecurityTokenTenantRunner implements TenantAware.TenantRunner { - - private final String controllerId; - private final String tenant; - - private GetSecurityTokenTenantRunner(final String tenant, final String controllerId) { - this.tenant = tenant; - this.controllerId = controllerId; - } - - @Override - public String run() { - LOGGER.trace("retrieving security token for controllerId {}", controllerId); - final SecurityContext oldContext = SecurityContextHolder.getContext(); - try { - SecurityContextHolder.setContext(getSecurityTokenReadContext()); - return controllerManagement.getSecurityTokenByControllerId(controllerId); - } finally { - SecurityContextHolder.setContext(oldContext); - } - } - - private SecurityContext getSecurityTokenReadContext() { - final SecurityContextImpl securityContextImpl = new SecurityContextImpl(); - securityContextImpl.setAuthentication(getSecurityTokenReadAuthentication()); - return securityContextImpl; - } - - private Authentication getSecurityTokenReadAuthentication() { - final AnonymousAuthenticationToken anonymousAuthenticationToken = new AnonymousAuthenticationToken( - "anonymous-read-security-token", "anonymous", com.google.common.collect.Lists - .newArrayList(new SimpleGrantedAuthority(SpPermission.READ_TARGET_SEC_TOKEN))); - anonymousAuthenticationToken.setDetails(new TenantAwareAuthenticationDetails(tenant, true)); - return anonymousAuthenticationToken; - } - } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java index 7a4138a2f..f6ff27dcc 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java @@ -9,8 +9,8 @@ package org.eclipse.hawkbit.ui.artifacts.details; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -46,6 +46,7 @@ import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; import com.google.common.base.Strings; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.server.FontAwesome; import com.vaadin.shared.ui.label.ContentMode; @@ -207,8 +208,7 @@ public class ArtifactDetailsLayout extends VerticalLayout { } private Container createArtifactLazyQueryContainer() { - final Map queryConfiguration = new HashMap<>(); - return getArtifactLazyQueryContainer(queryConfiguration); + return getArtifactLazyQueryContainer(Collections.emptyMap()); } private LazyQueryContainer getArtifactLazyQueryContainer(final Map queryConfig) { @@ -429,9 +429,12 @@ public class ArtifactDetailsLayout extends VerticalLayout { titleOfArtifactDetails.setContentMode(ContentMode.HTML); } } - final Map queryConfiguration = new HashMap<>(); + final Map queryConfiguration; if (baseSwModuleId != null) { + queryConfiguration = Maps.newHashMapWithExpectedSize(1); queryConfiguration.put(SPUIDefinitions.BY_BASE_SOFTWARE_MODULE, baseSwModuleId); + } else { + queryConfiguration = Collections.emptyMap(); } final LazyQueryContainer artifactContainer = getArtifactLazyQueryContainer(queryConfiguration); artifactDetailsTable.setContainerDataSource(artifactContainer); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java index 280d2ab43..2fabb3bb9 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/event/UploadViewAcceptCriteria.java @@ -9,13 +9,13 @@ package org.eclipse.hawkbit.ui.artifacts.event; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.hawkbit.ui.common.AbstractAcceptCriteria; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; +import com.google.common.collect.Maps; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.ViewScope; import com.vaadin.ui.Component; @@ -54,7 +54,7 @@ public class UploadViewAcceptCriteria extends AbstractAcceptCriteria { } private static Map> createDropConfigurations() { - final Map> config = new HashMap<>(); + final Map> config = Maps.newHashMapWithExpectedSize(1); // Delete drop area droppable components config.put(UIComponentIdProvider.DELETE_BUTTON_WRAPPER_ID, Arrays.asList( UIComponentIdProvider.UPLOAD_SOFTWARE_MODULE_TABLE, UIComponentIdProvider.UPLOAD_TYPE_BUTTON_PREFIX)); @@ -63,7 +63,7 @@ public class UploadViewAcceptCriteria extends AbstractAcceptCriteria { } private static Map createDropHintConfigurations() { - final Map config = new HashMap<>(); + final Map config = Maps.newHashMapWithExpectedSize(2); config.put(UIComponentIdProvider.UPLOAD_TYPE_BUTTON_PREFIX, UploadArtifactUIEvent.SOFTWARE_TYPE_DRAG_START); config.put(UIComponentIdProvider.UPLOAD_SOFTWARE_MODULE_TABLE, UploadArtifactUIEvent.SOFTWARE_DRAG_START); return config; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java index afb1591a4..3d875e450 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/footer/UploadViewConfirmationWindowLayout.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.ui.artifacts.footer; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -21,11 +20,12 @@ import org.eclipse.hawkbit.ui.artifacts.state.CustomFile; import org.eclipse.hawkbit.ui.common.confirmwindow.layout.AbstractConfirmationWindowLayout; import org.eclipse.hawkbit.ui.common.confirmwindow.layout.ConfirmationTab; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.springframework.beans.factory.annotation.Autowired; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.util.IndexedContainer; @@ -64,7 +64,7 @@ public class UploadViewConfirmationWindowLayout extends AbstractConfirmationWind @Override protected Map getConfimrationTabs() { - final Map tabs = new HashMap<>(); + final Map tabs = Maps.newHashMapWithExpectedSize(2); if (!artifactUploadState.getDeleteSofwareModules().isEmpty()) { tabs.put(i18n.get("caption.delete.swmodule.accordion.tab"), createSMDeleteConfirmationTab()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java index 3c4430326..0168ad3d9 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/smtable/SoftwareModuleTable.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.ui.artifacts.smtable; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -25,11 +24,11 @@ import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder; import org.eclipse.hawkbit.ui.distributions.smtable.SwMetadataPopupLayout; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.TableColumn; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer; @@ -37,6 +36,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -97,7 +97,7 @@ public class SoftwareModuleTable extends AbstractNamedVersionTable prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(2); artifactUploadState.getSoftwareModuleFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadConfirmationWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadConfirmationWindow.java index 098ebb2c4..c71a58367 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadConfirmationWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadConfirmationWindow.java @@ -32,11 +32,11 @@ import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmallNoBorder; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleTiny; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.I18N; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.SpringContextHelper; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -572,6 +572,9 @@ public class UploadConfirmationWindow implements Button.ClickListener { } + // Exception squid:S3655 - Optional access is checked in + // checkIfArtifactDetailsDispalyed subroutine + @SuppressWarnings("squid:S3655") private void processArtifactUpload() { final List itemIds = (List) uploadDetailsTable.getItemIds(); if (preUploadValidation(itemIds)) { @@ -593,6 +596,7 @@ public class UploadConfirmationWindow implements Button.ClickListener { } refreshArtifactDetailsLayout = checkIfArtifactDetailsDispalyed(bSoftwareModule.getId()); } + if (refreshArtifactDetailsLayout) { uploadLayout.refreshArtifactDetailsLayout(artifactUploadState.getSelectedBaseSoftwareModule().get()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java index e887cf020..744590030 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/upload/UploadLayout.java @@ -34,10 +34,10 @@ import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleSmall; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.I18N; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.eclipse.hawkbit.util.SPInfo; import org.slf4j.Logger; @@ -254,6 +254,11 @@ public class UploadLayout extends VerticalLayout { final Html5File[] files = ((WrapperTransferable) event.getTransferable()).getFiles(); // selected software module at the time of file drop is // considered for upload + + if (!artifactUploadState.getSelectedBaseSoftwareModule().isPresent()) { + return; + } + final SoftwareModule selectedSw = artifactUploadState.getSelectedBaseSoftwareModule().get(); // reset the flag hasDirectory = Boolean.FALSE; @@ -589,7 +594,9 @@ public class UploadLayout extends VerticalLayout { // delete file system zombies artifactUploadState.getFileSelected().forEach(customFile -> { final File file = new File(customFile.getFilePath()); - file.delete(); + if (!file.delete()) { + LOG.warn("Failed to delete file {} in upload dialog", customFile.getFilePath()); + } }); artifactUploadState.getFileSelected().clear(); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java index 418944582..3cc00e016 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/CommonDialogWindow.java @@ -12,7 +12,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -28,11 +27,12 @@ import org.eclipse.hawkbit.ui.decorators.SPUIButtonStyleNoBorderWithIcon; import org.eclipse.hawkbit.ui.layouts.AbstractCreateUpdateTagLayout; import org.eclipse.hawkbit.ui.management.targettable.TargetAddUpdateWindowLayout; import org.eclipse.hawkbit.ui.utils.I18N; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.vaadin.hene.flexibleoptiongroup.FlexibleOptionGroupItemComponent; import com.google.common.base.Strings; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.vaadin.data.Container.ItemSetChangeEvent; import com.vaadin.data.Container.ItemSetChangeListener; @@ -129,8 +129,8 @@ public class CommonDialogWindow extends Window { this.helpLink = helpLink; this.closeListener = closeListener; this.cancelButtonClickListener = cancelButtonClickListener; - this.orginalValues = new HashMap<>(); this.allComponents = getAllComponents(layout); + this.orginalValues = Maps.newHashMapWithExpectedSize(allComponents.size()); this.i18n = i18n; init(); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTable.java index fc560c2fa..e34964bb4 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/table/AbstractTable.java @@ -30,8 +30,8 @@ import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.spring.events.EventBus; -import com.google.gwt.thirdparty.guava.common.collect.Iterables; -import com.google.gwt.thirdparty.guava.common.collect.Sets; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.Transferable; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java index a2ded014d..b6f90e7ac 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java @@ -93,7 +93,7 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet private VerticalLayout tagsLayout; - private final Map assignedSWModule = new HashMap<>(); + private Map assignedSWModule; /** * softwareLayout Initialize the component. @@ -144,6 +144,10 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet } if (null != softwareModuleIdNameList) { + if (assignedSWModule == null) { + assignedSWModule = new HashMap<>(); + } + for (final SoftwareModuleIdName swIdName : softwareModuleIdNameList) { final SoftwareModule softwareModule = softwareManagement.findSoftwareModuleById(swIdName.getId()); if (assignedSWModule.containsKey(softwareModule.getType().getName())) { @@ -169,9 +173,11 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet } private Button assignSoftModuleButton(final String softwareModuleName) { - if (getPermissionChecker().hasUpdateDistributionPermission() && distributionSetManagement - .findDistributionSetById(manageDistUIState.getLastSelectedDistribution().get().getId()) - .getAssignedTargets().isEmpty()) { + if (getPermissionChecker().hasUpdateDistributionPermission() + && manageDistUIState.getLastSelectedDistribution().isPresent() + && distributionSetManagement + .findDistributionSetById(manageDistUIState.getLastSelectedDistribution().get().getId()) + .getAssignedTargets().isEmpty()) { final Button reassignSoftModule = SPUIComponentProvider.getButton(softwareModuleName, "", "", "", true, FontAwesome.TIMES, SPUIButtonStyleSmallNoBorder.class); reassignSoftModule.setEnabled(false); @@ -186,6 +192,9 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet @SuppressWarnings("unchecked") private void updateSoftwareModule(final SoftwareModule module) { + if (assignedSWModule == null) { + assignedSWModule = new HashMap<>(); + } softwareModuleTable.getContainerDataSource().getItemIds(); if (assignedSWModule.containsKey(module.getType().getName())) { @@ -363,7 +372,9 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet if ((saveActionWindowEvent == SaveActionWindowEvent.SAVED_ASSIGNMENTS || saveActionWindowEvent == SaveActionWindowEvent.DISCARD_ALL_ASSIGNMENTS) && getSelectedBaseEntity() != null) { - assignedSWModule.clear(); + if (assignedSWModule != null) { + assignedSWModule.clear(); + } setSelectedBaseEntity( distributionSetManagement.findDistributionSetByIdWithDetails(getSelectedBaseEntityId())); UI.getCurrent().access(() -> populateModule()); @@ -375,7 +386,9 @@ public class DistributionSetDetails extends AbstractNamedVersionedEntityTableDet if (saveActionWindowEvent == SaveActionWindowEvent.DISCARD_ASSIGNMENT || saveActionWindowEvent == SaveActionWindowEvent.DISCARD_ALL_ASSIGNMENTS || saveActionWindowEvent == SaveActionWindowEvent.DELETE_ALL_SOFWARE) { - assignedSWModule.clear(); + if (assignedSWModule != null) { + assignedSWModule.clear(); + } showUnsavedAssignment(); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java index 2a860c9b8..a615e62aa 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTable.java @@ -57,6 +57,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -167,7 +168,7 @@ public class DistributionSetTable extends AbstractNamedVersionTable prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(2); manageDistUIState.getManageDistFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); @@ -530,7 +531,7 @@ public class DistributionSetTable extends AbstractNamedVersionTable> createDropConfigurations() { - final Map> config = new HashMap<>(); + final Map> config = Maps.newHashMapWithExpectedSize(2); // Delete drop area droppable components config.put(UIComponentIdProvider.DELETE_BUTTON_WRAPPER_ID, @@ -82,7 +82,7 @@ public class DistributionsViewAcceptCriteria extends AbstractAcceptCriteria { } private static Map createDropHintConfigurations() { - final Map config = new HashMap<>(); + final Map config = Maps.newHashMapWithExpectedSize(4); config.put(SPUIDefinitions.DISTRIBUTION_TYPE_ID_PREFIXS, DragEvent.DISTRIBUTION_TYPE_DRAG); config.put(UIComponentIdProvider.DIST_TABLE_ID, DragEvent.DISTRIBUTION_DRAG); config.put(UIComponentIdProvider.UPLOAD_SOFTWARE_MODULE_TABLE, DragEvent.SOFTWAREMODULE_DRAG); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/footer/DistributionsConfirmationWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/footer/DistributionsConfirmationWindowLayout.java index c629d6a23..ab0b28249 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/footer/DistributionsConfirmationWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/footer/DistributionsConfirmationWindowLayout.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.ui.distributions.footer; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -37,6 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.util.IndexedContainer; @@ -90,7 +90,7 @@ public class DistributionsConfirmationWindowLayout extends AbstractConfirmationW @Override protected Map getConfimrationTabs() { - final Map tabs = new HashMap<>(); + final Map tabs = Maps.newHashMapWithExpectedSize(5); /* Create tab for SW Modules delete */ if (!manageDistUIState.getDeleteSofwareModulesList().isEmpty()) { tabs.put(i18n.get("caption.delete.swmodule.accordion.tab"), createSMDeleteConfirmationTab()); @@ -112,7 +112,6 @@ public class DistributionsConfirmationWindowLayout extends AbstractConfirmationW } /* Create tab for Assign Software Module */ - if (!manageDistUIState.getAssignedList().isEmpty()) { tabs.put(i18n.get("caption.assign.dist.accordion.tab"), createAssignSWModuleConfirmationTab()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleTable.java index d7186de6b..dd684009f 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleTable.java @@ -8,7 +8,6 @@ */ package org.eclipse.hawkbit.ui.distributions.smtable; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -40,6 +39,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -143,7 +143,7 @@ public class SwModuleTable extends AbstractNamedVersionTable prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(3); manageDistUIState.getSoftwareModuleFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtype/DistSMTypeFilterButtons.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtype/DistSMTypeFilterButtons.java index 41798d21d..b8d584b1b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtype/DistSMTypeFilterButtons.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtype/DistSMTypeFilterButtons.java @@ -13,8 +13,7 @@ import static org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent.Sof import static org.eclipse.hawkbit.ui.utils.UIComponentIdProvider.SW_MODULE_TYPE_TABLE_ID; import static org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions.VAR_NAME; -import java.util.HashMap; -import java.util.Map; +import java.util.Collections; import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleTypeEvent; import org.eclipse.hawkbit.ui.common.SoftwareModuleTypeBeanQuery; @@ -59,10 +58,9 @@ public class DistSMTypeFilterButtons extends AbstractFilterButtons { @Override protected LazyQueryContainer createButtonsLazyQueryContainer() { - final Map queryConfig = new HashMap<>(); final BeanQueryFactory typeQF = new BeanQueryFactory<>( SoftwareModuleTypeBeanQuery.class); - typeQF.setQueryConfiguration(queryConfig); + typeQF.setQueryConfiguration(Collections.emptyMap()); return new LazyQueryContainer(new LazyQueryDefinition(true, 20, VAR_NAME), typeQF); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/state/ManageDistUIState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/state/ManageDistUIState.java index caac53147..3a2075afc 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/state/ManageDistUIState.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/state/ManageDistUIState.java @@ -52,7 +52,7 @@ public class ManageDistUIState implements ManagmentEntityState selectedSoftwareModules = emptySet(); - private Set selectedDeleteDistSetTypes = new HashSet<>(); + private final Set selectedDeleteDistSetTypes = new HashSet<>(); private Set selectedDeleteSWModuleTypes = new HashSet<>(); @@ -201,10 +201,6 @@ public class ManageDistUIState implements ManagmentEntityState selectedDeleteDistSetTypes) { - this.selectedDeleteDistSetTypes = selectedDeleteDistSetTypes; - } - public Set getSelectedDeleteSWModuleTypes() { return selectedDeleteSWModuleTypes; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/FilterExpression.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/FilterExpression.java deleted file mode 100644 index 853c6f095..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/FilterExpression.java +++ /dev/null @@ -1,27 +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.ui.filter; - -/** - * A filter expression interface definition to implement the UI filter - * mechanism. The filter expression can evaluate if e.g. Targets should - * currently be added to the target list or if the current enabled filtered will - * filter the target and not show the newly created target. - * - */ -@FunctionalInterface -public interface FilterExpression { - - /** - * @return {@code true} if the expression evaluate that it should be - * filtered and not shown on the UI, otherwise {@code false} - */ - boolean doFilter(); - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/Filters.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/Filters.java deleted file mode 100644 index 4b79a5654..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/Filters.java +++ /dev/null @@ -1,77 +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.ui.filter; - -import java.util.Arrays; -import java.util.List; - -/** - * {@link Filters} which provides the functionality to combine - * {@link FilterExpression}s. - * - * - * - * @see FilterExpression - * - */ -public final class Filters { - - /** - * private. - */ - private Filters() { - - } - - /** - * Combines the given filter to an or-expression and evaluate them. - * - * @param expressions - * the expressions to combine with an or-filter - * @return an or-combined filter expression - */ - public static FilterExpression or(final List expressions) { - return or(expressions.toArray(new FilterExpression[expressions.size()])); - } - - /** - * Combines the given filter to an or-expression and evaluate them. - * - * @param expressions - * the expressions to combine with an or-filter - * @return an or-combined filter expression - */ - public static FilterExpression or(final FilterExpression... expressions) { - return new OrFilterExpression(expressions); - } - - private static final class OrFilterExpression implements FilterExpression { - - private final FilterExpression[] expresssions; - - private OrFilterExpression(final FilterExpression[] expresssions) { - this.expresssions = Arrays.copyOf(expresssions, expresssions.length); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.server.ui.filter.FilterExpression#evaluate() - */ - @Override - public boolean doFilter() { - for (final FilterExpression filterExpression : expresssions) { - if (filterExpression.doFilter()) { - return true; - } - } - return false; - } - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/CustomTargetFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/CustomTargetFilter.java deleted file mode 100644 index b0aadc01d..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/CustomTargetFilter.java +++ /dev/null @@ -1,48 +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.ui.filter.target; - -import java.util.Optional; - -import org.eclipse.hawkbit.repository.model.TargetFilterQuery; -import org.eclipse.hawkbit.ui.filter.FilterExpression; - -/** - * Checks if custom target filter is applied. - * - */ -public class CustomTargetFilter implements FilterExpression { - - private final Optional targetFilterQuery; - - /** - * Initialize. - * - * @param targetFilterQuery - * custom target filter applied - */ - public CustomTargetFilter(final Optional targetFilterQuery) { - this.targetFilterQuery = targetFilterQuery; - - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.ui.filter.FilterExpression#doFilter() - */ - @Override - public boolean doFilter() { - if (!targetFilterQuery.isPresent()) { - return false; - } - return true; - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetSearchTextFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetSearchTextFilter.java deleted file mode 100644 index 64e9067e9..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetSearchTextFilter.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. - */ -package org.eclipse.hawkbit.ui.filter.target; - -import java.net.URI; - -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.ui.filter.FilterExpression; - -/** - * - * - * - */ -public class TargetSearchTextFilter implements FilterExpression { - - private final Target target; - private final String searchTextUpper; - - /** - * @param target - * the target to check against the search text - * @param searchText - * the search text check against the given target - */ - public TargetSearchTextFilter(final Target target, final String searchText) { - this.target = target; - this.searchTextUpper = searchText.toUpperCase(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.server.ui.filter.FilterExpression#evaluate() - */ - @Override - public boolean doFilter() { - return !(descriptionIgnoreCase() || nameIgnoreCase() || controllerIdIgnoreCase() || ipAddressIgnoreCase()); - } - - private boolean descriptionIgnoreCase() { - if (target.getDescription() == null) { - return false; - } - return target.getDescription().toUpperCase().contains(searchTextUpper); - } - - private boolean nameIgnoreCase() { - if (target.getName() == null) { - return false; - } - return target.getName().toUpperCase().contains(searchTextUpper); - } - - private boolean controllerIdIgnoreCase() { - return target.getControllerId().toUpperCase().contains(searchTextUpper); - } - - private boolean ipAddressIgnoreCase() { - final URI targetAddress = target.getTargetInfo().getAddress(); - if (targetAddress == null || targetAddress.getHost() == null) { - return false; - } - return targetAddress.getHost().toUpperCase().contains(searchTextUpper); - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetStatusFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetStatusFilter.java deleted file mode 100644 index eee7156e3..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetStatusFilter.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. - */ -package org.eclipse.hawkbit.ui.filter.target; - -import java.util.List; - -import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; -import org.eclipse.hawkbit.ui.filter.FilterExpression; - -/** - * - * - * - */ -public class TargetStatusFilter implements FilterExpression { - - private final List targetUpdateStatus; - - /** - * @param target - * the target to check the update status against - * @param updateStatus - * the target update status to check against the given target - */ - public TargetStatusFilter(final List updateStatus) { - this.targetUpdateStatus = updateStatus; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.server.ui.filter.FilterExpression#evaluate() - */ - @Override - public boolean doFilter() { - if (targetUpdateStatus.isEmpty()) { - return false; - } - return true; - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetTagFilter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetTagFilter.java deleted file mode 100644 index 5516c279e..000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filter/target/TargetTagFilter.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved. - */ -package org.eclipse.hawkbit.ui.filter.target; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.ui.filter.FilterExpression; -import org.springframework.util.CollectionUtils; - -/** - * - * - * - */ -public class TargetTagFilter implements FilterExpression { - - private final Target target; - private final Collection tags; - private final boolean noTag; - - /** - * @param target - * the target to check the filter against - * @param tags - * the tags to check the target against it - * @param noTag - * {@code true} indicates that targets which have no tags should - * not be filtered, otherwise {@code false} - */ - public TargetTagFilter(final Target target, final Collection tags, final boolean noTag) { - this.target = target; - this.tags = tags; - this.noTag = noTag; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.hawkbit.server.ui.filter.FilterExpression#evaluate() - */ - @Override - public boolean doFilter() { - final List targetTags = target.getTags().stream().map(targetTag -> targetTag.getName()) - .collect(Collectors.toList()); - if (targetTags.isEmpty() || (noTag && targetTags.isEmpty())) { - return false; - } - return !CollectionUtils.containsAny(targetTags, tags); - } -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterTable.java index 49117fb31..9d82316e1 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterTable.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.ui.filtermanagement; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -37,6 +36,7 @@ import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; import com.google.common.base.Strings; +import com.google.common.collect.Maps; import com.vaadin.data.Item; import com.vaadin.server.FontAwesome; import com.vaadin.shared.ui.label.ContentMode; @@ -142,7 +142,7 @@ public class CreateOrUpdateFilterTable extends Table { } private Map prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(2); if (!Strings.isNullOrEmpty(filterManagementUIState.getFilterQueryValue())) { queryConfig.put(SPUIDefinitions.FILTER_BY_QUERY, filterManagementUIState.getFilterQueryValue()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java index 199420442..a7b1c9929 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/TargetFilterTable.java @@ -38,6 +38,7 @@ import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.server.FontAwesome; @@ -128,7 +129,7 @@ public class TargetFilterTable extends Table { } private Map prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(1); filterManagementUIState.getCustomFilterSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); return queryConfig; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java index d0bcc7dab..2446be8f0 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java @@ -8,9 +8,8 @@ */ package org.eclipse.hawkbit.ui.management.dstable; -import java.util.HashMap; +import java.util.Collections; import java.util.HashSet; -import java.util.Map; import java.util.Set; import javax.annotation.PostConstruct; @@ -178,10 +177,9 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent { * @return */ private LazyQueryContainer getDistSetTypeLazyQueryContainer() { - final Map queryConfig = new HashMap<>(); final BeanQueryFactory dtQF = new BeanQueryFactory<>( DistributionSetTypeBeanQuery.class); - dtQF.setQueryConfiguration(queryConfig); + dtQF.setQueryConfiguration(Collections.emptyMap()); final LazyQueryContainer disttypeContainer = new LazyQueryContainer( new LazyQueryDefinition(true, SPUIDefinitions.DIST_TYPE_SIZE, SPUILabelDefinitions.VAR_NAME), dtQF); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java index 97da2be3b..728e1036b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.ui.management.dstable; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -43,11 +42,11 @@ import org.eclipse.hawkbit.ui.management.event.PinUnpinEvent; import org.eclipse.hawkbit.ui.management.event.SaveActionWindowEvent; import org.eclipse.hawkbit.ui.management.state.ManagementUIState; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; import org.eclipse.hawkbit.ui.utils.TableColumn; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; @@ -56,6 +55,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -240,7 +240,7 @@ public class DistributionTable extends AbstractNamedVersionTable prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(4); managementUIState.getDistributionTableFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); managementUIState.getDistributionTableFilters().getPinnedTargetId() @@ -525,6 +525,10 @@ public class DistributionTable extends AbstractNamedVersionTable queryConfig = new HashMap<>(); final BeanQueryFactory tagQF = new BeanQueryFactory<>(DistributionTagBeanQuery.class); - tagQF.setQueryConfiguration(queryConfig); + tagQF.setQueryConfiguration(Collections.emptyMap()); return HawkbitCommonUtil.createDSLazyQueryContainer( new BeanQueryFactory(DistributionTagBeanQuery.class)); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/event/ManagementViewAcceptCriteria.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/event/ManagementViewAcceptCriteria.java index 862988350..51145a170 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/event/ManagementViewAcceptCriteria.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/event/ManagementViewAcceptCriteria.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.ui.management.event; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -17,6 +16,7 @@ import org.eclipse.hawkbit.ui.common.AbstractAcceptCriteria; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; +import com.google.common.collect.Maps; import com.vaadin.spring.annotation.SpringComponent; import com.vaadin.spring.annotation.ViewScope; import com.vaadin.ui.Component; @@ -67,7 +67,7 @@ public class ManagementViewAcceptCriteria extends AbstractAcceptCriteria { } private static Map> createDropConfigurations() { - final Map> config = new HashMap<>(); + final Map> config = Maps.newHashMapWithExpectedSize(6); // Delete drop area acceptable components config.put(UIComponentIdProvider.DELETE_BUTTON_WRAPPER_ID, @@ -94,7 +94,7 @@ public class ManagementViewAcceptCriteria extends AbstractAcceptCriteria { } private static Map createDropHintConfigurations() { - final Map config = new HashMap<>(); + final Map config = Maps.newHashMapWithExpectedSize(4); config.put(SPUIDefinitions.TARGET_TAG_ID_PREFIXS, DragEvent.TARGET_TAG_DRAG); config.put(UIComponentIdProvider.TARGET_TABLE_ID, DragEvent.TARGET_DRAG); config.put(UIComponentIdProvider.DIST_TABLE_ID, DragEvent.DISTRIBUTION_DRAG); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java index 7182087c9..0ce1923d4 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/footer/ManangementConfirmationWindowLayout.java @@ -10,7 +10,6 @@ package org.eclipse.hawkbit.ui.management.footer; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -39,6 +38,7 @@ import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.springframework.beans.factory.annotation.Autowired; +import com.google.common.collect.Maps; import com.vaadin.data.Item; import com.vaadin.data.util.IndexedContainer; import com.vaadin.server.FontAwesome; @@ -88,7 +88,7 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin @Override protected Map getConfimrationTabs() { - final Map tabs = new HashMap<>(); + final Map tabs = Maps.newHashMapWithExpectedSize(3); if (!managementUIState.getDeletedDistributionList().isEmpty()) { tabs.put(i18n.get("caption.delete.dist.accordion.tab"), createDeletedDistributionTab()); } @@ -150,7 +150,7 @@ public class ManangementConfirmationWindowLayout extends AbstractConfirmationWin ? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime() : RepositoryModelConstants.NO_FORCE_TIME; - final Map> saveAssignedList = new HashMap<>(); + final Map> saveAssignedList = Maps.newHashMapWithExpectedSize(itemIds.size()); int successAssignmentCount = 0; int duplicateAssignmentCount = 0; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java index ac3261895..2f40f5def 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java @@ -87,10 +87,9 @@ public class ManagementUIState implements ManagmentEntityState queryConfiguration = new HashMap<>(); + final Map queryConfiguration = Maps.newHashMapWithExpectedSize(2); final List list = new ArrayList<>(); queryConfiguration.put(SPUIDefinitions.FILTER_BY_NO_TAG, diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java index cc4ad98bc..f47118f68 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java @@ -19,10 +19,10 @@ import static org.eclipse.hawkbit.ui.management.event.TargetFilterEvent.REMOVE_F import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -43,12 +43,6 @@ import org.eclipse.hawkbit.ui.common.ManagmentEntityState; import org.eclipse.hawkbit.ui.common.UserDetailsFormatter; import org.eclipse.hawkbit.ui.common.table.AbstractTable; import org.eclipse.hawkbit.ui.common.table.BaseEntityEventType; -import org.eclipse.hawkbit.ui.filter.FilterExpression; -import org.eclipse.hawkbit.ui.filter.Filters; -import org.eclipse.hawkbit.ui.filter.target.CustomTargetFilter; -import org.eclipse.hawkbit.ui.filter.target.TargetSearchTextFilter; -import org.eclipse.hawkbit.ui.filter.target.TargetStatusFilter; -import org.eclipse.hawkbit.ui.filter.target.TargetTagFilter; import org.eclipse.hawkbit.ui.management.event.DragEvent; import org.eclipse.hawkbit.ui.management.event.ManagementUIEvent; import org.eclipse.hawkbit.ui.management.event.ManagementViewAcceptCriteria; @@ -78,6 +72,7 @@ import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; import com.google.common.base.Strings; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.event.dd.DragAndDropEvent; @@ -140,11 +135,14 @@ public class TargetTable extends AbstractTable { if (TargetCreatedEvent.class.isInstance(firstEvent)) { onTargetCreatedEvents(); } else if (TargetInfoUpdateEvent.class.isInstance(firstEvent)) { - onTargetInfoUpdateEvents((List) events); + onTargetUpdateEvents(((List) events).stream() + .map(targetInfoUpdateEvent -> targetInfoUpdateEvent.getEntity().getTarget()) + .collect(Collectors.toList())); } else if (TargetDeletedEvent.class.isInstance(firstEvent)) { onTargetDeletedEvent((List) events); } else if (TargetUpdatedEvent.class.isInstance(firstEvent)) { - onTargetUpdateEvents((List) events); + onTargetUpdateEvents(((List) events).stream() + .map(targetInfoUpdateEvent -> targetInfoUpdateEvent.getEntity()).collect(Collectors.toList())); } } @@ -348,7 +346,7 @@ public class TargetTable extends AbstractTable { } private Map prepareQueryConfigFilters() { - final Map queryConfig = new HashMap<>(); + final Map queryConfig = Maps.newHashMapWithExpectedSize(7); managementUIState.getTargetTableFilters().getSearchText() .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); managementUIState.getTargetTableFilters().getDistributionSet() @@ -746,16 +744,17 @@ public class TargetTable extends AbstractTable { } @SuppressWarnings("unchecked") - private void updateVisibleItemOnEvent(final TargetInfo targetInfo, final Target target, - final TargetIdName targetIdName) { + private void updateVisibleItemOnEvent(final TargetInfo targetInfo) { + final Target target = targetInfo.getTarget(); + final TargetIdName targetIdName = target.getTargetIdName(); + final LazyQueryContainer targetContainer = (LazyQueryContainer) getContainerDataSource(); final Item item = targetContainer.getItem(targetIdName); + item.getItemProperty(SPUILabelDefinitions.VAR_NAME).setValue(target.getName()); - if (targetInfo != null) { - item.getItemProperty(SPUILabelDefinitions.VAR_POLL_STATUS_TOOL_TIP) - .setValue(HawkbitCommonUtil.getPollStatusToolTip(targetInfo.getPollStatus(), i18n)); - item.getItemProperty(SPUILabelDefinitions.VAR_TARGET_STATUS).setValue(targetInfo.getUpdateStatus()); - } + item.getItemProperty(SPUILabelDefinitions.VAR_POLL_STATUS_TOOL_TIP) + .setValue(HawkbitCommonUtil.getPollStatusToolTip(targetInfo.getPollStatus(), i18n)); + item.getItemProperty(SPUILabelDefinitions.VAR_TARGET_STATUS).setValue(targetInfo.getUpdateStatus()); } private boolean isLastSelectedTarget(final TargetIdName targetIdName) { @@ -767,62 +766,30 @@ public class TargetTable extends AbstractTable { * EventListener method which is called by the event bus to notify about a * list of {@link TargetInfoUpdateEvent}. * - * @param targetInfoUpdateEvents - * list of target info update event + * @param updatedTargets + * list of updated targets */ - private void onTargetInfoUpdateEvents(final List targetInfoUpdateEvents) { + private void onTargetUpdateEvents(final List updatedTargets) { @SuppressWarnings("unchecked") final List visibleItemIds = (List) getVisibleItemIds(); - boolean shoulTargetsUpdated = false; - Target lastSelectedTarget = null; - for (final TargetInfoUpdateEvent targetInfoUpdateEvent : targetInfoUpdateEvents) { - final TargetInfo targetInfo = targetInfoUpdateEvent.getEntity(); - final Target target = targetInfo.getTarget(); - final TargetIdName targetIdName = target.getTargetIdName(); - if (Filters.or(getTargetTableFilters(target)).doFilter()) { - shoulTargetsUpdated = true; - } else { - if (visibleItemIds.contains(targetIdName)) { - updateVisibleItemOnEvent(targetInfo, target, targetIdName); - } - } - // workaround until push is available for action history, re-select - // the updated target so the action history gets refreshed. - if (isLastSelectedTarget(targetIdName)) { - lastSelectedTarget = target; - } - } - if (shoulTargetsUpdated) { - refreshTargets(); - } - if (lastSelectedTarget != null) { - eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.SELECTED_ENTITY, lastSelectedTarget)); - } - } - private void onTargetUpdateEvents(final List events) { - final List visibleItemIds = (List) getVisibleItemIds(); - boolean shoulTargetsUpdated = false; - Target lastSelectedTarget = null; - for (final TargetUpdatedEvent targetUpdatedEvent : events) { - final Target target = targetUpdatedEvent.getEntity(); - final TargetIdName targetIdName = target.getTargetIdName(); - if (Filters.or(getTargetTableFilters(target)).doFilter()) { - shoulTargetsUpdated = true; - } else { - if (visibleItemIds.contains(targetIdName)) { - updateVisibleItemOnEvent(null, target, targetIdName); - } - } - if (isLastSelectedTarget(targetIdName)) { - lastSelectedTarget = target; - } - } - if (shoulTargetsUpdated) { + if (isFilterEnabled()) { + LOG.debug("Filter enabled on UI {}. Refresh targets from database.", getUI().getUIId()); refreshTargets(); + } else { + updatedTargets.stream().filter(target -> visibleItemIds.contains(target.getTargetIdName())) + .forEach(target -> updateVisibleItemOnEvent(target.getTargetInfo())); } - if (lastSelectedTarget != null) { - eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.SELECTED_ENTITY, lastSelectedTarget)); + + // workaround until push is available for action + // history, re-select + // the updated target so the action history gets + // refreshed. + final Optional selected = updatedTargets.stream() + .filter(target -> isLastSelectedTarget(target.getTargetIdName())).findAny(); + if (selected.isPresent()) { + LOG.debug("Selected element has changed on UI {}. Reselect to update action history.", getUI().getUIId()); + eventBus.publish(this, new TargetTableEvent(BaseEntityEventType.SELECTED_ENTITY, selected.get())); } } @@ -830,17 +797,11 @@ public class TargetTable extends AbstractTable { refreshTargets(); } - private List getTargetTableFilters(final Target target) { + private boolean isFilterEnabled() { final TargetTableFilters targetTableFilters = managementUIState.getTargetTableFilters(); - final List filters = new ArrayList<>(); - if (targetTableFilters.getSearchText().isPresent()) { - filters.add(new TargetSearchTextFilter(target, targetTableFilters.getSearchText().get())); - } - filters.add(new TargetStatusFilter(targetTableFilters.getClickedStatusTargetTags())); - filters.add(new TargetTagFilter(target, targetTableFilters.getClickedTargetTags(), - targetTableFilters.isNoTagSelected())); - filters.add(new CustomTargetFilter(targetTableFilters.getTargetFilterQuery())); - return filters; + return targetTableFilters.getSearchText().isPresent() || !targetTableFilters.getClickedTargetTags().isEmpty() + || !targetTableFilters.getClickedStatusTargetTags().isEmpty() + || targetTableFilters.getTargetFilterQuery().isPresent(); } /** diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetFilterQueryButtons.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetFilterQueryButtons.java index cca0a1abc..a0c723248 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetFilterQueryButtons.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettag/TargetFilterQueryButtons.java @@ -9,9 +9,8 @@ package org.eclipse.hawkbit.ui.management.targettag; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; import java.util.List; -import java.util.Map; import javax.annotation.PreDestroy; @@ -102,8 +101,7 @@ public class TargetFilterQueryButtons extends Table { protected LazyQueryContainer createButtonsLazyQueryContainer() { final BeanQueryFactory queryFactory = new BeanQueryFactory<>( TargetFilterBeanQuery.class); - final Map queryConfig = new HashMap<>(); - queryFactory.setQueryConfiguration(queryConfig); + queryFactory.setQueryConfiguration(Collections.emptyMap()); return new LazyQueryContainer(new LazyQueryDefinition(true, 20, "id"), queryFactory); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java index fb3dc6c9f..c35378e20 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java @@ -296,7 +296,7 @@ public final class DashboardMenu extends CustomComponent { final Optional findFirst = dashboardVaadinViews.stream() .filter(view -> view.getViewName().equals(viewName)).findFirst(); - if (findFirst == null || !findFirst.isPresent()) { + if (!findFirst.isPresent()) { return null; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java index 023979ead..31de959ae 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/push/DelayedEventBusPushStrategy.java @@ -8,11 +8,11 @@ */ package org.eclipse.hawkbit.ui.push; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.BlockingDeque; -import java.util.concurrent.Executors; +import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.eclipse.hawkbit.eventbus.event.EntityEvent; +import org.eclipse.hawkbit.eventbus.event.Event; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.ui.UIEventProvider; import org.slf4j.Logger; @@ -57,10 +58,11 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { private static final Logger LOG = LoggerFactory.getLogger(DelayedEventBusPushStrategy.class); private static final int BLOCK_SIZE = 10_000; - private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); - private final BlockingDeque queue = new LinkedBlockingDeque<>(BLOCK_SIZE); + private final ScheduledExecutorService executorService; + private final BlockingDeque queue = new LinkedBlockingDeque<>(BLOCK_SIZE); private final EventBus.SessionEventBus eventBus; private final com.google.common.eventbus.EventBus systemEventBus; + private int uiid = -1; private ScheduledFuture jobHandle; @@ -68,15 +70,20 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { /** * Constructor. - * + * + * @param executorService + * for scheduled execution of event forwarding to the UI * @param eventBus * the session event bus to where the events should be dispatched * @param systemEventBus * the system event bus where to retrieve the events from the * back-end + * @param eventProvider + * for event delegation to UI */ - public DelayedEventBusPushStrategy(final SessionEventBus eventBus, + public DelayedEventBusPushStrategy(final ScheduledExecutorService executorService, final SessionEventBus eventBus, final com.google.common.eventbus.EventBus systemEventBus, final UIEventProvider eventProvider) { + this.executorService = executorService; this.eventBus = eventBus; this.systemEventBus = systemEventBus; this.eventProvider = eventProvider; @@ -92,7 +99,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { */ @Subscribe @AllowConcurrentEvents - public void dispatch(final org.eclipse.hawkbit.eventbus.event.Event event) { + public void dispatch(final Event event) { // to dispatch too many events which are not interested on the UI if (!isEventProvided(event)) { LOG.trace("Event is not supported in the UI!!! Dropped event is {}", event); @@ -100,19 +107,24 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { } if (!queue.offer(event)) { - LOG.warn("Deque limit is reached, cannot add more events!!! Dropped event is {}", event); + LOG.trace("Deque limit is reached, cannot add more events for UI {}! Dropped event is {}", uiid, event); return; } } - private boolean isEventProvided(final org.eclipse.hawkbit.eventbus.event.Event event) { + private boolean isEventProvided(final Event event) { return eventProvider.getSingleEvents().contains(event.getClass()) || eventProvider.getBulkEvents().contains(event.getClass()); } @Override public void init(final UI vaadinUI) { - LOG.debug("Initialize delayed event push strategy"); + uiid = vaadinUI.getUIId(); + LOG.info("Initialize delayed event push strategy for UI {}", uiid); + if (vaadinUI.getSession() == null) { + LOG.error("Vaadin session of UI {} is null! Event push disabled!", uiid); + } + jobHandle = executorService.scheduleWithFixedDelay(new DispatchRunnable(vaadinUI, vaadinUI.getSession()), 500, 2000, TimeUnit.MILLISECONDS); systemEventBus.register(this); @@ -120,10 +132,9 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { @Override public void clean() { - LOG.debug("Cleanup resources"); - jobHandle.cancel(true); + LOG.info("Cleanup delayed event push strategy for UI", uiid); systemEventBus.unregister(this); - executorService.shutdownNow(); + jobHandle.cancel(true); queue.clear(); } @@ -138,8 +149,7 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { * @return {@code true} if the event can be dispatched to the UI otherwise * {@code false} */ - protected boolean eventSecurityCheck(final SecurityContext userContext, - final org.eclipse.hawkbit.eventbus.event.Event event) { + protected static boolean eventSecurityCheck(final SecurityContext userContext, final Event event) { if (userContext == null || userContext.getAuthentication() == null) { return false; } @@ -163,43 +173,39 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { @Override public void run() { - LOG.debug("UI EventBus aggregator started"); + LOG.debug("UI EventBus aggregator started for UI {}", vaadinUI.getUIId()); final long timestamp = System.currentTimeMillis(); - final List events = new LinkedList<>(); - for (int i = 0; i < BLOCK_SIZE; i++) { - final org.eclipse.hawkbit.eventbus.event.Event pollEvent = queue.poll(); - if (pollEvent == null) { - continue; - } - events.add(pollEvent); + + final int size = queue.size(); + if (size <= 0) { + LOG.debug("UI EventBus aggregator for UI {} has nothing to do.", vaadinUI.getUIId()); + return; } + final List events = new ArrayList<>(size); + final int eventsSize = queue.drainTo(events); + if (events.isEmpty()) { + LOG.debug("UI EventBus aggregator for UI {} has nothing to do.", vaadinUI.getUIId()); return; } - if (vaadinSession == null) { - return; - } - - LOG.debug("UI EventBus aggregator session: {}", vaadinSession); - final WrappedSession wrappedSession = vaadinSession.getSession(); if (wrappedSession == null) { return; } - final int eventsSize = events.size(); + LOG.debug("UI EventBus aggregator dispatches {} events for session {} for UI {}", eventsSize, vaadinSession, + vaadinUI.getUIId()); doDispatch(events, wrappedSession); - LOG.debug("UI EventBus aggregator done with sending {} events in {} ms", eventsSize, - System.currentTimeMillis() - timestamp); + LOG.debug("UI EventBus aggregator done with sending {} events in {} ms for UI {}", eventsSize, + System.currentTimeMillis() - timestamp, vaadinUI.getUIId()); } - private void doDispatch(final List events, - final WrappedSession wrappedSession) { + private void doDispatch(final List events, final WrappedSession wrappedSession) { final SecurityContext userContext = (SecurityContext) wrappedSession .getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); final SecurityContext oldContext = SecurityContextHolder.getContext(); @@ -210,25 +216,24 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { if (vaadinSession.getState() != State.OPEN) { return; } + LOG.debug("UI EventBus aggregator of UI {} got lock on session.", vaadinUI.getUIId()); fowardSingleEvents(events, userContext); fowardBulkEvents(events, userContext); - }); + LOG.debug("UI EventBus aggregator of UI {} left lock on session.", vaadinUI.getUIId()); + }).get(); + } catch (InterruptedException | ExecutionException e) { + LOG.error("Wait for Vaadin session for UI {} interrupted!", vaadinUI.getUIId(), e); } finally { SecurityContextHolder.setContext(oldContext); } } - private void fowardBulkEvents(final List events, - final SecurityContext userContext) { + private void fowardBulkEvents(final List events, final SecurityContext userContext) { final Set> filterBulkEvenTypes = eventProvider.getFilteredBulkEventsType(events); - publishBulkEvent(events, userContext, filterBulkEvenTypes); - } - private void publishBulkEvent(final List events, - final SecurityContext userContext, final Set> filterBulkEvenTypes) { for (final Class bulkType : filterBulkEvenTypes) { - final List listBulkEvents = events.stream() - .filter(event -> DelayedEventBusPushStrategy.this.eventSecurityCheck(userContext, event) + final List listBulkEvents = events.stream() + .filter(event -> DelayedEventBusPushStrategy.eventSecurityCheck(userContext, event) && bulkType.isInstance(event)) .collect(Collectors.toList()); if (!listBulkEvents.isEmpty()) { @@ -237,10 +242,9 @@ public class DelayedEventBusPushStrategy implements EventPushStrategy { } } - private void fowardSingleEvents(final List events, - final SecurityContext userContext) { + private void fowardSingleEvents(final List events, final SecurityContext userContext) { events.stream() - .filter(event -> DelayedEventBusPushStrategy.this.eventSecurityCheck(userContext, event) + .filter(event -> DelayedEventBusPushStrategy.eventSecurityCheck(userContext, event) && eventProvider.getSingleEvents().contains(event.getClass())) .forEach(event -> eventBus.publish(vaadinUI, event)); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java index fd551a9b1..dae322b2e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/AddUpdateRolloutWindowLayout.java @@ -43,10 +43,10 @@ import org.eclipse.hawkbit.ui.rollout.event.RolloutEvent; import org.eclipse.hawkbit.ui.utils.HawkbitCommonUtil; import org.eclipse.hawkbit.ui.utils.I18N; import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; -import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions; import org.eclipse.hawkbit.ui.utils.SPUIStyleDefinitions; +import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.springframework.beans.factory.annotation.Autowired; import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; @@ -610,26 +610,15 @@ public class AddUpdateRolloutWindowLayout extends GridLayout { @Override public void validate(final Object value) { - try { - if (isNoOfGroupsOrTargetFilterEmpty()) { - uiNotification - .displayValidationError(i18n.get("message.rollout.noofgroups.or.targetfilter.missing")); - } else { - if (value != null) { - final int groupSize = getGroupSize(); - new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, groupSize), 0, - groupSize).validate(Integer.valueOf(value.toString())); - } + if (isNoOfGroupsOrTargetFilterEmpty()) { + uiNotification.displayValidationError(i18n.get("message.rollout.noofgroups.or.targetfilter.missing")); + } else { + if (value != null) { + final int groupSize = getGroupSize(); + new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, groupSize), 0, groupSize) + .validate(Integer.valueOf(value.toString())); } } - // suppress the need of preserve original exception, will blow - // up the - // log and not necessary here - catch (final InvalidValueException ex) { - // we have to throw the exception here, otherwise the UI won't - // show the vaadin validation error! - throw ex; - } } private boolean isNoOfGroupsOrTargetFilterEmpty() { @@ -648,19 +637,9 @@ public class AddUpdateRolloutWindowLayout extends GridLayout { @Override public void validate(final Object value) { - try { - if (value != null) { - new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 100), 0, 100) - .validate(Integer.valueOf(value.toString())); - } - } - // suppress the need of preserve original exception, will blow - // up the - // log and not necessary here - catch (final InvalidValueException ex) { - // we have to throw the exception here, otherwise the UI won't - // show the vaadin validation error! - throw ex; + if (value != null) { + new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 100), 0, 100) + .validate(Integer.valueOf(value.toString())); } } } @@ -670,19 +649,9 @@ public class AddUpdateRolloutWindowLayout extends GridLayout { @Override public void validate(final Object value) { - try { - if (value != null) { - new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 500), 0, 500) - .validate(Integer.valueOf(value.toString())); - } - } - // suppress the need of preserve original exception, will blow - // up the - // log and not necessary here - catch (final InvalidValueException ex) { - // we have to throw the exception here, otherwise the UI won't - // show the vaadin validation error! - throw ex; + if (value != null) { + new IntegerRangeValidator(i18n.get(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 0, 500), 0, 500) + .validate(Integer.valueOf(value.toString())); } } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java index bbbc96d09..12980aeca 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java @@ -21,7 +21,6 @@ import static org.eclipse.hawkbit.ui.utils.SPUILabelDefinitions.VAR_TOTAL_TARGET import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -57,6 +56,7 @@ import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; +import com.google.common.collect.Maps; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.util.converter.Converter; @@ -495,7 +495,8 @@ public class RolloutListGrid extends AbstractGrid { * Contains all expected rollout status per column to enable or disable * the button. */ - private static final Map EXPECTED_ROLLOUT_STATUS_ENABLE_BUTTON = new HashMap<>(); + private static final Map EXPECTED_ROLLOUT_STATUS_ENABLE_BUTTON = Maps + .newHashMapWithExpectedSize(2); private final Container.Indexed containerDataSource; static { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/HawkbitCommonUtil.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/HawkbitCommonUtil.java index d8e93350b..105886fb2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/HawkbitCommonUtil.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/HawkbitCommonUtil.java @@ -9,8 +9,8 @@ package org.eclipse.hawkbit.ui.utils; import java.util.Arrays; +import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.Map; import java.util.TimeZone; @@ -435,8 +435,7 @@ public final class HawkbitCommonUtil { */ public static LazyQueryContainer createLazyQueryContainer( final BeanQueryFactory> queryFactory) { - final Map queryConfig = new HashMap<>(); - queryFactory.setQueryConfiguration(queryConfig); + queryFactory.setQueryConfiguration(Collections.emptyMap()); return new LazyQueryContainer(new LazyQueryDefinition(true, 20, SPUILabelDefinitions.VAR_NAME), queryFactory); } @@ -448,8 +447,7 @@ public final class HawkbitCommonUtil { */ public static LazyQueryContainer createDSLazyQueryContainer( final BeanQueryFactory> queryFactory) { - final Map queryConfig = new HashMap<>(); - queryFactory.setQueryConfiguration(queryConfig); + queryFactory.setQueryConfiguration(Collections.emptyMap()); return new LazyQueryContainer(new LazyQueryDefinition(true, 20, "tagIdName"), queryFactory); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPDateTimeUtil.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPDateTimeUtil.java index 49bae86e1..df8cf27d6 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPDateTimeUtil.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPDateTimeUtil.java @@ -11,7 +11,6 @@ package org.eclipse.hawkbit.ui.utils; import java.text.SimpleDateFormat; import java.time.ZoneId; import java.util.Date; -import java.util.HashMap; import java.util.Map; import java.util.TimeZone; @@ -19,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DurationFormatUtils; import org.eclipse.hawkbit.repository.model.BaseEntity; +import com.google.common.collect.Maps; import com.vaadin.server.WebBrowser; /** @@ -32,7 +32,7 @@ import com.vaadin.server.WebBrowser; public final class SPDateTimeUtil { private static final String DURATION_FORMAT = "y','M','d','H','m','s"; - private static final Map DURATION_I18N = new HashMap<>(); + private static final Map DURATION_I18N = Maps.newHashMapWithExpectedSize(6); static { DURATION_I18N.put(0, CalendarI18N.YEAR); diff --git a/pom.xml b/pom.xml index 043b4e614..2084b92f6 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,46 @@ ${release.scm.url} + + Hudson + https://hudson.eclipse.org/hawkbit/ + + + + + kaizimmerm + kai.zimmermann@bosch-si.com + Bosch Software Innovations GmbH + https://www.bosch-si.com + + Lead + Committer + + + + michahirsch + michael.hirsch@bosch-si.com + Bosch Software Innovations GmbH + https://www.bosch-si.com + + Committer + + + + + + + ossrh + hawkBit Repository - Release + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + ossrh + hawkBit Repository - Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + + vaadin-addons @@ -60,14 +100,14 @@ 1.8 - 1.3.7.RELEASE + true - + 1.6.1.RELEASE 4.1.2.RELEASE - + 3.2.2 @@ -114,7 +154,7 @@ https://github.com/eclipse/hawkbit.git - + https://sonar.eu-gb.mybluemix.net eclipse/hawkbit https://projects.eclipse.org/projects/iot.hawkbit @@ -125,7 +165,7 @@ ${jacoco.outputDir}/${jacoco.out.ut.file} jacoco-it.exec ${jacoco.outputDir}/${jacoco.out.it.file} - + @@ -135,11 +175,11 @@ org.apache.maven.plugins maven-compiler-plugin - -Xlint:all - true - true + -Xlint:all + true + true - + com.mycila license-maven-plugin @@ -164,6 +204,32 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + 1.4.1 + + + + enforce-no-snapshots + + enforce + + + ${snapshotDependencyAllowed} + + + No Snapshots Allowed! + + + No Snapshots Allowed! + + + + + + org.codehaus.mojo versions-maven-plugin @@ -218,7 +284,7 @@ - + @@ -238,7 +304,7 @@ - + @@ -252,7 +318,7 @@ - + @@ -323,13 +389,71 @@ ${jacoco.version} - org.bsc.maven - maven-processor-plugin - ${maven.processor.plugin.version} + org.bsc.maven + maven-processor-plugin + ${maven.processor.plugin.version} + + + nexus_staging + + + !skipNexusStaging + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + false + + + + + + + + create_gpg_signature + + false + + createGPGSignature + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + @@ -496,7 +620,7 @@ - + org.apache.commons commons-lang3 ${commons-lang3.version} diff --git a/sonarCircleCi.sh b/sonarCircleCi.sh index cf03e5593..9a00961c8 100644 --- a/sonarCircleCi.sh +++ b/sonarCircleCi.sh @@ -10,7 +10,7 @@ echo $CI_PULL_REQUEST pull request # regular sonar on master if [ "$CIRCLE_BRANCH" = "master" ]; then - mvn verify license:check sonar:sonar -Dsonar.login=$SONAR_SERVER_USER -Dsonar.password=$SONAR_SERVER_PASSWD + mvn verify license:check sonar:sonar -Dsonar.login=$SONAR_SERVER_USER -Dsonar.password=$SONAR_SERVER_PASSWD -Dsonar.exclusions=**/target/generated-sources/apt/**,**/src/test/**,**/src/main/java/org/eclipse/hawkbit/repository/test/** -Dsonar.coverage.exclusions=**/src/main/java/org/eclipse/hawkbit/ui/**,**/target/generated-sources/apt/**,**/src/main/java/org/eclipse/hawkbit/repository/test/** # preview in case of pull request - disabled as circle does not fill those with pull reuqests from different directories else #if [ -n "$CI_PULL_REQUEST" ]; then