diff --git a/docs/src/main/resources/_data/doclinks.yml b/docs/src/main/resources/_data/doclinks.yml index cc12a5b8e..b104b4a8e 100644 --- a/docs/src/main/resources/_data/doclinks.yml +++ b/docs/src/main/resources/_data/doclinks.yml @@ -17,6 +17,8 @@ href: "/documentation/architecture/datamodel.html" - title: "Target States" href: "/documentation/architecture/targetstate.html" + - title: "Rollout Management" + href: "/documentation/architecture/rollout-management.html" - title: "Interfaces" href: "" @@ -48,4 +50,4 @@ - title: "Theme Customization" href: "/documentation/guide/customtheme.html" - title: "Create Feign Client" - href: "/documentation/guide/feignclient.html" \ No newline at end of file + href: "/documentation/guide/feignclient.html" diff --git a/docs/src/main/resources/documentation/architecture/rollout-management.md b/docs/src/main/resources/documentation/architecture/rollout-management.md new file mode 100644 index 000000000..9b7a1792c --- /dev/null +++ b/docs/src/main/resources/documentation/architecture/rollout-management.md @@ -0,0 +1,45 @@ +--- +layout: documentation +title: Rollout Management +--- + +{% include base.html %} + +Software update operations in large scale IoT scenarios with hundreds of thousands of devices require special handling. + +That includes: +- _Technical Scalability_ by means of horizontal scale of the _Rollouts_ server cluster in the cloud. +- _Global_ artifact _content delivery_ capacities. +- _Functional Scalability_ by means of: + - Secure handling of large volumes of devices at rollout creation time. + - Monitoring of the rollout progress. + - Emergency rollout shutdown in case of problems on to many devices. + +- Reporting capabilities for a complete understanding of the rollout progress at each point in time. + +Eclipse _hawkBit_ sees these capabilities under the term Rollout Management. + +The following capabilities are currently supported by the _Rollout Management_: +- Create, update and start of rollouts. + - Selection of targets as input for the rollout based on _target filter_ functionality. + - Selection of a _DistributionSet_. + - Auto-splitting of the input target list into a defined number deployment groups. + +- Cascading start of the deployment groups based on installation status of the previous group. +- Emergency shutdown of the rollout in case a group exceeds the defined error threshold. +- Rollout progress monitoring for the entire rollout and the individual groups. + + +## Cascading Deployment Group Execution +The cascading execution of the deployment groups is based on two thresholds that can be defined by the rollout creator. +- success condition by means of percentage of successfully installed targets in the current groups triggers. +- error condition by means of absolute or percentage of failed installations which triggers an emergency shutdown of the entire rollout. + +[[images/DeploymentGroups.png]] + +## Rollout state machine +### State Machine on Rollout +![](../images/rolloutstatediagram.png){:width="100%" .image-center} + +### State Machine on Rollout Deployment Group +![](../images/rolloutgroupstatediagram.png){:width="100%" .image-center} diff --git a/docs/src/main/resources/documentation/guide/clustering.md b/docs/src/main/resources/documentation/guide/clustering.md index 4c589bf93..a4ff6d383 100644 --- a/docs/src/main/resources/documentation/guide/clustering.md +++ b/docs/src/main/resources/documentation/guide/clustering.md @@ -9,7 +9,7 @@ title: Clustering _hawkBit_ is able to run in a cluster with some constraints. This guide provides insights in the basic concepts and how to setup your own cluster. You can find additional information in the [hawkbit example app's README](https://github.com/eclipse/hawkbit/blob/master/examples/hawkbit-example-app/README.md). -# Big picture +# Big picture ![](../images/overall_cluster.png){:width="100%"} @@ -18,7 +18,7 @@ _hawkBit_ is able to run in a cluster with some constraints. This guide provides Event communication between nodes is based on [Spring Cloud Bus](https://cloud.spring.io/spring-cloud-bus/) and [Spring Cloud Stream](http://docs.spring.io/spring-cloud-stream/docs/current/reference/htmlsingle/). There are different [binder implementations](http://docs.spring.io/spring-cloud-stream/docs/current/reference/htmlsingle/#_binders) available. The _hawkbit example app_ uses RabbitMQ binder. Every node gets his own queue to receive cluster events, the default payload is JSON. If an event is thrown locally at one node, it will be automatically delivered to all other available nodes via the Spring Cloud Bus's topic exchange: -![](../images/eventing-within-cluster.png){:width="100%"} +[[images/eventing-within-cluster.png]] Via the ServiceMatcher you can check whether an event happened locally at one node or on a different node. `serviceMatcher.isFromSelf(event)` @@ -26,7 +26,7 @@ Via the ServiceMatcher you can check whether an event happened locally at one no # Caching Every node is maintaining its own caches independent from other nodes. So there is no globally shared/synchronized cache instance within the cluster. In order to keep nodes in sync a TTL (time to live) can be set for all caches to ensure that after some time the cache is refreshed from the database. To enable the TTL just set the property "hawkbit.cache.global.ttl" (value in milliseconds). -Of course you can implement a shared cache, e.g. Redis. +Of course you can implement a shared cache, e.g. Redis. See [CacheAutoConfiguration](https://github.com/eclipse/hawkbit/blob/master/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/cache/CacheAutoConfiguration.java) # Schedulers @@ -36,7 +36,7 @@ Every node has multiple schedulers which run after a defined period of time. All # Known constraints ## UI sessions -As of today _hawkBit_ isn't storing user sessions in a shared, clusterwide cache. Session is only bound to the node where the login took place. If this node is going down for whatever reason, the session is lost and the user is forced to login again. +As of today _hawkBit_ isn't storing user sessions in a shared, clusterwide cache. Session is only bound to the node where the login took place. If this node is going down for whatever reason, the session is lost and the user is forced to login again. In case that's not an option, you can help yourself by introducing a shared session cache based on e.g. Redis. Furthermore _hawkBit_ isn't supporting session stickiness out of the box either. However most of the well known load balancers out there can solve this issue. @@ -49,4 +49,3 @@ In a cluster-capable environment this fact can lead to issues as it could happen _hawkbit_ owns the feature of guarding itself from DoS attacks, a [DoS filter](https://github.com/eclipse/hawkbit/blob/master/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/DosFilter.java). It reduces the maximum number of requests per seconds which can be configured for read and write requests. This mechanism is only working for every node separately, i.e. in a cluster environment the worst-case behaviour would be that the maximum number of requests per seconds will be increased to its product if every request is handled by a different node. The same constraint exists with the validator to check if a user tried too many logins within a defined period of time. - diff --git a/docs/src/main/resources/documentation/images/rolloutgroupstatediagram.png b/docs/src/main/resources/documentation/images/rolloutgroupstatediagram.png new file mode 100644 index 000000000..6a4728120 Binary files /dev/null and b/docs/src/main/resources/documentation/images/rolloutgroupstatediagram.png differ diff --git a/docs/src/main/resources/documentation/images/rolloutstatediagram.png b/docs/src/main/resources/documentation/images/rolloutstatediagram.png new file mode 100644 index 000000000..6fb273173 Binary files /dev/null and b/docs/src/main/resources/documentation/images/rolloutstatediagram.png differ diff --git a/docs/src/main/resources/documentation/interfaces/management-api.md b/docs/src/main/resources/documentation/interfaces/management-api.md index 21d075ffc..f7c0dacfb 100644 --- a/docs/src/main/resources/documentation/interfaces/management-api.md +++ b/docs/src/main/resources/documentation/interfaces/management-api.md @@ -36,7 +36,7 @@ Available Management APIs resources are: * [Software Module Types](https://docs.bosch-iot-rollouts.com/documentation/rest-api/softwaremoduletypes-api-guide.html) * [Target Tag](https://docs.bosch-iot-rollouts.com/documentation/rest-api/targettag-api-guide.html) * [Distribution Set Tag](https://docs.bosch-iot-rollouts.com/documentation/rest-api/distributionsettag-api-guide.html) -* [Rollouts](http://https://docs.bosch-iot-rollouts.com/documentation/developerguide/apispecifications/managementapi/rollouts.html) +* [Rollouts](https://docs.bosch-iot-rollouts.com/documentation/rest-api/rollout-api-guide.html) ## Headers @@ -74,4 +74,4 @@ A _Distribution Set_ entity may have for example URIs to artifacts, _Software Mo "metadata": { "href": "http://localhost:8080/rest/v1/softwaremodules/83/metadata?offset=0&limit=50" } -{% endhighlight %} \ No newline at end of file +{% endhighlight %} 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 a78fcf812..ba441bbb7 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 @@ -16,6 +16,7 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import org.apache.commons.lang3.ArrayUtils; import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata; import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact; import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifactHash; @@ -116,7 +117,7 @@ public final class MgmtSoftwareModuleMapper { response.add(linkTo(methodOn(MgmtSoftwareModuleResource.class).getMetadata(response.getModuleId(), Integer.parseInt(MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET), Integer.parseInt(MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT), null, null)) - .withRel("metadata")); + .withRel("metadata").expand(ArrayUtils.toArray())); return response; } 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 f593b12be..5ef0e726c 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 @@ -19,6 +19,7 @@ import java.util.Date; import java.util.List; import java.util.stream.Collectors; +import org.apache.commons.lang3.ArrayUtils; import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtAction; import org.eclipse.hawkbit.mgmt.json.model.action.MgmtActionStatus; @@ -65,7 +66,7 @@ public final class MgmtTargetMapper { response.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionHistory(response.getControllerId(), 0, MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, ActionFields.ID.getFieldName() + ":" + SortDirection.DESC, null)) - .withRel(MgmtRestConstants.TARGET_V1_ACTIONS)); + .withRel(MgmtRestConstants.TARGET_V1_ACTIONS).expand(ArrayUtils.toArray())); } static void addPollStatus(final Target target, final MgmtTarget targetRest) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/pom.xml b/hawkbit-repository/hawkbit-repository-jpa/pom.xml index d1f275f81..7398d24db 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/pom.xml +++ b/hawkbit-repository/hawkbit-repository-jpa/pom.xml @@ -81,10 +81,6 @@ cz.jirutka.rsql rsql-parser - - org.apache.commons - commons-collections4 - 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 dd8f25c11..5798a29db 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 @@ -18,7 +18,6 @@ import java.util.stream.Collectors; import javax.persistence.EntityManager; -import org.apache.commons.collections4.CollectionUtils; import org.eclipse.hawkbit.repository.DistributionSetFields; import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetMetadataFields; @@ -69,6 +68,7 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.Modifying; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; import org.springframework.validation.annotation.Validated; import com.google.common.base.Strings; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetCreate.java index 6865f82ca..e5fb559c8 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetCreate.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetCreate.java @@ -12,7 +12,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Optional; -import org.apache.commons.collections4.CollectionUtils; import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.builder.AbstractDistributionSetUpdateCreate; @@ -21,6 +20,7 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.springframework.util.CollectionUtils; /** * Create/build implementation. diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeCreate.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeCreate.java index a71732646..f7b4ff6b3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeCreate.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/builder/JpaDistributionSetTypeCreate.java @@ -11,13 +11,13 @@ package org.eclipse.hawkbit.repository.jpa.builder; import java.util.Collection; import java.util.Collections; -import org.apache.commons.collections4.CollectionUtils; import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.builder.AbstractDistributionSetTypeUpdateCreate; import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; +import org.springframework.util.CollectionUtils; /** * Create/build implementation. 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 cded4d4d5..5d55e5aa6 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 @@ -25,12 +25,12 @@ 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.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.hibernate.validator.constraints.NotEmpty; +import org.springframework.util.CollectionUtils; /** * A distribution set type defines which software module types can or have to be diff --git a/hawkbit-ui/pom.xml b/hawkbit-ui/pom.xml index b0d8d09b6..008581f9a 100644 --- a/hawkbit-ui/pom.xml +++ b/hawkbit-ui/pom.xml @@ -204,10 +204,6 @@ org.springframework.security spring-security-web - - org.apache.commons - commons-collections4 - com.vaadin 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 2adcef834..93a6d2e9e 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 @@ -19,7 +19,6 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.hawkbit.ui.artifacts.smtable.SoftwareModuleAddUpdateWindow; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; @@ -33,7 +32,6 @@ 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; import com.vaadin.data.Property.ValueChangeEvent; @@ -240,7 +238,7 @@ public class CommonDialogWindow extends Window { Object value = field.getValue(); if (field instanceof Table) { - value = Sets.newHashSet(((Table) field).getContainerDataSource().getItemIds()); + value = ((Table) field).getContainerDataSource().getItemIds(); } orginalValues.put(field, value); } @@ -263,6 +261,9 @@ public class CommonDialogWindow extends Window { } protected void addComponentListeners() { + // avoid duplicate registration + removeListeners(); + for (final AbstractField field : allComponents) { if (field instanceof TextChangeNotifier) { ((TextChangeNotifier) field).addTextChangeListener(new ChangeListener(field)); @@ -290,28 +291,13 @@ public class CommonDialogWindow extends Window { } final Object currentValue = getCurrentVaue(currentChangedComponent, newValue, field); - if (!isValueEquals(field, originalValue, currentValue)) { + if (!Objects.equals(originalValue, currentValue)) { return true; } } return false; } - private static boolean isValueEquals(final AbstractField field, final Object orginalValue, - final Object currentValue) { - if (Set.class.equals(field.getType())) { - return CollectionUtils.isEqualCollection(CollectionUtils.emptyIfNull((Collection) orginalValue), - CollectionUtils.emptyIfNull((Collection) currentValue)); - } - - if (String.class.equals(field.getType())) { - return Objects.equals(Strings.emptyToNull((String) orginalValue), - Strings.emptyToNull((String) currentValue)); - } - - return Objects.equals(orginalValue, currentValue); - } - private static Object getCurrentVaue(final Component currentChangedComponent, final Object newValue, final AbstractField field) { Object currentValue = field.getValue(); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetBeanQuery.java index 1271fe166..285362a5b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetBeanQuery.java @@ -20,7 +20,6 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import org.apache.commons.collections4.CollectionUtils; import org.eclipse.hawkbit.repository.FilterParams; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.TargetManagement; @@ -39,6 +38,7 @@ import org.eclipse.hawkbit.ui.utils.SpringContextHelper; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; +import org.springframework.util.CollectionUtils; import org.vaadin.addons.lazyquerycontainer.AbstractBeanQuery; import org.vaadin.addons.lazyquerycontainer.QueryDefinition; @@ -211,7 +211,7 @@ public class TargetBeanQuery extends AbstractBeanQuery { private boolean isAnyFilterSelected() { final boolean isFilterSelected = isTagSelected() || isOverdueFilterEnabled(); - return isFilterSelected || CollectionUtils.isNotEmpty(status) || distributionId != null + return isFilterSelected || !CollectionUtils.isEmpty(status) || distributionId != null || !isNullOrEmpty(searchText); } 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 94f0d427f..cfd9a4a4e 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 @@ -26,7 +26,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.collections4.CollectionUtils; import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.FilterParams; import org.eclipse.hawkbit.repository.TagManagement; @@ -70,6 +69,7 @@ import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import org.vaadin.addons.lazyquerycontainer.BeanQueryFactory; import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer; import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition; diff --git a/pom.xml b/pom.xml index 5bf1d5303..29931c176 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 1.4.3.RELEASE + 1.4.4.RELEASE org.eclipse.hawkbit @@ -101,11 +101,12 @@ 1.8 - 1.4.3.RELEASE + 1.4.4.RELEASE true + 0.23.0.RELEASE 2.0.0 @@ -137,14 +138,13 @@ 1.1.6 1.0.2 19.0 - 1.5.3 + 1.5.7 1.50.5 2.2.4 1.1.7 1.1 1.1.1 3.4 - 4.1 20141113 2.1.0 Camden.SR1 @@ -668,11 +668,6 @@ commons-lang3 ${commons-lang3.version} - - org.apache.commons - commons-collections4 - ${commons-collections4.version} - org.springframework.boot spring-boot-starter-test