You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:26:04 UTC
[sling-org-apache-sling-discovery-commons] 01/38: SLING-4665 :
adding patch provided by Timothee Maret, many thanks
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.discovery.commons-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-discovery-commons.git
commit b8fe30ce3973f15a94a2115a3f39721ced7e6d37
Author: Stefan Egli <st...@apache.org>
AuthorDate: Wed Apr 29 08:30:03 2015 +0000
SLING-4665 : adding patch provided by Timothee Maret, many thanks
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/discovery/commons@1676687 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 82 ++++
.../sling/discovery/commons/InstancesDiff.java | 486 +++++++++++++++++++++
.../sling/discovery/commons/package-info.java | 29 ++
.../sling/discovery/commons/InstancesDiffTest.java | 331 ++++++++++++++
4 files changed, 928 insertions(+)
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..7b74420
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>22</version>
+ <relativePath>../../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.sling.discovery.commons</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.0.0-SNAPSHOT</version>
+
+ <name>Apache Sling Discovery Commons Bundle</name>
+ <description>
+ Commons services related to Sling Discovery.
+ </description>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/discovery/commons</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/discovery/commons</developerConnection>
+ <url>http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/discovery/commons</url>
+ </scm>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>biz.aQute</groupId>
+ <artifactId>bndlib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.discovery.api</artifactId>
+ <version>1.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <version>2.0.0</version>
+ </dependency>
+ <!-- Testing -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/discovery/commons/InstancesDiff.java b/src/main/java/org/apache/sling/discovery/commons/InstancesDiff.java
new file mode 100644
index 0000000..61b2f9c
--- /dev/null
+++ b/src/main/java/org/apache/sling/discovery/commons/InstancesDiff.java
@@ -0,0 +1,486 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.discovery.commons;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.InstanceFilter;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyView;
+
+/**
+ * The {@code InstancesDiff} allows to combine and filter two collections of {@code InstanceDescription} instances,
+ * an "old" collection and a "new" collection.<p>
+ *
+ * The comparison between {@code InstanceDescription} instances is done only on the basis of the Sling identifier.
+ * Two instances with the same Sling identifier are considered as equal.<p>
+ *
+ * <b>Note</b>: Each collection must contain only unique instances (no two instances with the same Sling identifier).
+ * Using the {@code InstancesDiff} with collections containing duplicated Sling id
+ * will throw an {@code IllegalArgumentException}.<p>
+ *
+ * @since 1.0.0
+ */
+public final class InstancesDiff {
+
+ /**
+ * A filter that keeps local instances (see {@link InstanceDescription#isLocal()}.
+ */
+ private static final InstanceFilter LOCAL_INSTANCE = new LocalInstanceFilter();
+
+ /**
+ * A filter that filters out local instances (see {@link InstanceDescription#isLocal()}.
+ */
+ private static final InstanceFilter NOT_LOCAL_INSTANCE = new NotFilter(LOCAL_INSTANCE);
+
+ /**
+ * A filter that keeps leader instances (see {@link InstanceDescription#isLeader()}.
+ */
+ private static final InstanceFilter LEADER_INSTANCE = new LeaderInstanceFilter();
+
+ /**
+ * A filter that filters out leader instances (see {@link InstanceDescription#isLeader()}.
+ */
+ private static final InstanceFilter NOT_LEADER_INSTANCE = new NotFilter(LEADER_INSTANCE);
+
+ /**
+ * Keeps track of the old {@code InstanceDescription} instances
+ *
+ * The map keys are the instance Sling identifiers and values are
+ * the {@code InstanceDescription} instances descriptions.
+ */
+ private final Map<String, InstanceDescription> oldInstances;
+
+ /**
+ * Keeps track of the new {@code InstanceDescription} instances
+ *
+ * The map keys are the instance Sling identifiers and values are
+ * the {@code InstanceDescription} instances descriptions.
+ */
+ private final Map<String, InstanceDescription> newInstances;
+
+ /**
+ * Create a new {@code InstancesDiff} based on the instances from the old and
+ * new {@code TopologyView} topology views contained in the {@code TopologyEvent} event provided.
+ *
+ * @param event the non {@code null} event from which the old and new topology views are used for computing.
+ * If either of the topology views are {@code null}, then they will be substituted by an
+ * empty collection of instances.
+ * @throws IllegalArgumentException if either of the collections contains duplicated Sling identifiers.
+ */
+ public InstancesDiff(@Nonnull TopologyEvent event) {
+ this(instancesOrEmpty(event.getOldView()), instancesOrEmpty(event.getNewView()));
+ }
+
+ /**
+ * Create a new {@code InstancesDiff} based on the instances from the old and
+ * new {@code TopologyView} topology views provided.
+ *
+ * @param oldView the non {@code null} old topology view from which the old collection is used for computing.
+ * @param newView the non {@code null} new topology view form which the new collection is used for computing.
+ * @throws IllegalArgumentException if either of the collections contains duplicated Sling identifiers.
+ */
+ public InstancesDiff(@Nonnull TopologyView oldView, @Nonnull TopologyView newView) {
+ this(oldView.getInstances(), newView.getInstances());
+ }
+
+ /**
+ * Create a new {@code InstancesDiff} based on the instances from the old and
+ * new {@code ClusterView} cluster views provided.
+ *
+ * @param oldView the non {@code null} old cluster view used for computing.
+ * @param newView the non {@code null} new cluster view used for computing.
+ * @throws IllegalArgumentException if either of the collections contains duplicated Sling identifiers.
+ */
+ public InstancesDiff(@Nonnull ClusterView oldView, @Nonnull ClusterView newView) {
+ this(oldView.getInstances(), newView.getInstances());
+ }
+
+ /**
+ * Create a new {@code InstancesDiff} based on the provided old and
+ * new {@code Collection} collections of instances.
+ *
+ * @param oldInstances the non {@code null} old collection of instances used for computing.
+ * @param newInstances the non {@code null} new collection of instances used for computing.
+ * @param <T> the type of instance which must extend {@code InstanceDescription}.
+ * @throws IllegalArgumentException if either of the collections contains duplicated Sling identifiers.
+ */
+ public <T extends InstanceDescription> InstancesDiff(@Nonnull Collection<T> oldInstances, @Nonnull Collection<T> newInstances) {
+ this.newInstances = getInstancesMap(newInstances);
+ this.oldInstances = getInstancesMap(oldInstances);
+ }
+
+ /**
+ * Returns the {@code InstanceSet} set containing the {@code InstanceDescription} instances that are
+ * contained in either the old or the new collection.<p>
+ *
+ * For {@code InstanceDescription} instances contained in both the old and
+ * the new collections, the method will retain those from either of the collections
+ * depending on the parameter #retainFromNewView.<p>
+ *
+ * @param retainFromNewCollection {@code true} in order to retain the instances from the new collection ;
+ * {@code false} in order to retain the instances from the old collection.
+ * @return the {@code InstanceCollection} collection containing the {@code InstanceDescription} instances
+ * from both collections.
+ */
+ @Nonnull
+ public InstanceCollection all(boolean retainFromNewCollection) {
+ return new InstanceCollection(partitionAll(retainFromNewCollection));
+ }
+
+ /**
+ * Returns the {@code InstanceCollection} collection containing the {@code InstanceDescription} instances that are
+ * contained in the new collection but not in the old collection.
+ *
+ * @return the {@code InstanceCollection} collection containing the instances in the new
+ * topology collection but not in the old collection.
+ */
+ @Nonnull
+ public InstanceCollection added() {
+ return new InstanceCollection(partitionAdded());
+ }
+
+ /**
+ * Returns the {@code InstanceCollection} collection containing the {@code InstanceDescription} instances that are
+ * contained in the old collection but not in the new collection.
+ *
+ * @return the {@code InstanceSet} set containing the instances in the old collection but not in the new collection.
+ */
+ @Nonnull
+ public InstanceCollection removed() {
+ return new InstanceCollection(partitionRemoved());
+ }
+
+ /**
+ * Returns the {@code InstanceSet} collection containing the {@code InstanceDescription} instances that are
+ * contained in both the old collection and the new collection.<p>
+ *
+ * The method will retain the {@code InstanceDescription} instances from either of the collections
+ * depending on the parameter #retainFromNewView.<p>
+ *
+ * @param retainFromNewCollection {@code true} in order to retain the instances from the new collection ;
+ * {@code false} in order to retain the instances from the old collection.
+ * @return the {@code InstanceCollection} collection containing the {@code InstanceDescription} instances
+ * contained in both collections.
+ */
+ @Nonnull
+ public InstanceCollection retained(boolean retainFromNewCollection) {
+ return new InstanceCollection(partitionRetained(retainFromNewCollection));
+ }
+
+ /**
+ * Returns the {@code InstanceCollection} collection containing the {@code InstanceDescription} instances that are
+ * contained in both the old and the new collections.<p>
+ *
+ * The method will retain the {@code InstanceDescription} instances from either of the collections
+ * depending on the parameter #retainFromNewView.<p>
+ *
+ * @param retainFromNewCollection {@code true} in order to retain the instances from the new collection ;
+ * {@code false} in order to retain the instances from the old collection.
+ * @param propertyChanged {@code true} in order to keep only the instances which
+ * properties have not changed between the old and new collections ;
+ * {@code false} in order to keep only the instances which properties have changed.
+ * @return the {@code InstanceCollection} collection containing the {@code InstanceDescription} instances
+ * contained in both views.
+ */
+ @Nonnull
+ public InstanceCollection retained(boolean retainFromNewCollection, boolean propertyChanged) {
+ return new InstanceCollection(partitionRetained(retainFromNewCollection, propertyChanged));
+ }
+
+ //
+
+ @Nonnull
+ private Map<String, InstanceDescription> partitionAll(boolean retainFromNewCollection) {
+ Map<String, InstanceDescription> partition = new HashMap<String, InstanceDescription>();
+ if (retainFromNewCollection) {
+ partition.putAll(oldInstances);
+ partition.putAll(newInstances);
+ } else {
+ partition.putAll(newInstances);
+ partition.putAll(oldInstances);
+ }
+ return partition;
+ }
+
+ @Nonnull
+ private Map<String, InstanceDescription> partitionRemoved() {
+ Map<String, InstanceDescription> partition = new HashMap<String, InstanceDescription>(oldInstances);
+ partition.keySet().removeAll(newInstances.keySet());
+ return partition;
+ }
+
+ @Nonnull
+ private Map<String, InstanceDescription> partitionAdded() {
+ Map<String, InstanceDescription> partition = new HashMap<String, InstanceDescription>(newInstances);
+ partition.keySet().removeAll(oldInstances.keySet());
+ return partition;
+ }
+
+ @Nonnull
+ private Map<String, InstanceDescription> partitionRetained(boolean retainFromNewCollection, boolean propertyChanged) {
+ Map<String, InstanceDescription> partition = new HashMap<String, InstanceDescription>();
+ for (Map.Entry<String, InstanceDescription> oldEntry : oldInstances.entrySet()) {
+ String slingId = oldEntry.getKey();
+ InstanceDescription newDescription = newInstances.get(slingId);
+ if(newDescription != null) {
+ InstanceDescription oldDescription = oldEntry.getValue();
+ boolean propertiesSame = newDescription.getProperties().equals(oldDescription.getProperties());
+ if ((propertiesSame && ! propertyChanged) || (! propertiesSame && propertyChanged)) {
+ partition.put(slingId, retainFromNewCollection ? newDescription : oldDescription);
+ }
+ }
+ }
+ return partition;
+ }
+
+ @Nonnull
+ private Map<String, InstanceDescription> partitionRetained(boolean retainFromNewCollection) {
+ Map<String, InstanceDescription> partition = new HashMap<String, InstanceDescription>();
+ if (retainFromNewCollection) {
+ partition.putAll(newInstances);
+ partition.keySet().retainAll(oldInstances.keySet());
+ } else {
+ partition.putAll(oldInstances);
+ partition.keySet().retainAll(newInstances.keySet());
+ }
+ return partition;
+ }
+
+ @Nonnull
+ private static Set<InstanceDescription> instancesOrEmpty(@Nullable TopologyView topologyView) {
+ return (topologyView != null) ? topologyView.getInstances() : Collections.<InstanceDescription>emptySet();
+ }
+
+ @Nonnull
+ private static <T extends InstanceDescription> Map<String, InstanceDescription> getInstancesMap(@Nonnull Collection<T> instances) {
+ Map<String, InstanceDescription> instancesMap = new HashMap<String, InstanceDescription>();
+ for (InstanceDescription instance : instances) {
+ String slingId = instance.getSlingId();
+ if (slingId != null) {
+ if (instancesMap.put(slingId, instance) != null) {
+ throw new IllegalArgumentException(String.format("Duplicated instance found for slingId: %s", slingId));
+ }
+ }
+ }
+ return instancesMap;
+ }
+
+ private static final class NotFilter implements InstanceFilter {
+
+ final InstanceFilter filter;
+
+ private NotFilter(InstanceFilter filter) {
+ this.filter = filter;
+ }
+
+ public boolean accept(InstanceDescription instance) {
+ return ! filter.accept(instance);
+ }
+ }
+
+ private static final class LocalInstanceFilter implements InstanceFilter {
+
+ public boolean accept(InstanceDescription instance) {
+ return instance.isLocal();
+ }
+ }
+
+ private static final class LeaderInstanceFilter implements InstanceFilter {
+
+ public boolean accept(InstanceDescription instance) {
+ return instance.isLeader();
+ }
+ }
+
+ private static final class InClusterView implements InstanceFilter {
+
+ private final ClusterView view;
+
+ private InClusterView(ClusterView view) {
+ this.view = view;
+ }
+
+ public boolean accept(InstanceDescription instance) {
+ return view.getId().equals(instance.getClusterView().getId());
+ }
+ }
+
+ /**
+ * The {@code InstanceCollection} collection allows to filter the instances using a set of custom filter
+ * either implementing {@code InstanceFilter} or pre-defined ones.<p>
+ *
+ * Filters conditions are joined combined together using the logical operator "AND".<p>
+ */
+ public final class InstanceCollection {
+
+ /**
+ * Holds the instances to be filtered.
+ *
+ * The map keys are the instance Sling identifiers and values are
+ * the {@code InstanceDescription} instances descriptions.
+ */
+ private final Map<String, InstanceDescription> instances;
+
+ /**
+ * Holds the set of filters to be applied (ANDed).
+ */
+ private final Set<InstanceFilter> filters = new HashSet<InstanceFilter>();
+
+ /**
+ * Filter the instances with a custom {@code InstanceFilter} filter.
+ *
+ * @param filter the filter to be applied on the instances
+ * @return {@code this}
+ */
+ @Nonnull
+ public InstanceCollection filterWith(@Nullable InstanceFilter filter) {
+ if (filter != null) {
+ filters.add(filter);
+ }
+ return this;
+ }
+
+ /**
+ * Keep only the local instance (see {@link InstanceDescription#isLocal()}.
+ *
+ * @return {@code this}
+ */
+ @Nonnull
+ public InstanceCollection isLocal() {
+ filters.add(LOCAL_INSTANCE);
+ return this;
+ }
+
+ /**
+ * Filter out the local instances (see {@link InstanceDescription#isLocal()}.
+ *
+ * @return {@code this}
+ */
+ @Nonnull
+ public InstanceCollection isNotLocal() {
+ filters.add(NOT_LOCAL_INSTANCE);
+ return this;
+ }
+
+ /**
+ * Keep only the leader instances (see {@link InstanceDescription#isLeader()}.
+ *
+ * @return {@code this}
+ */
+ @Nonnull
+ public InstanceCollection isLeader() {
+ filters.add(LEADER_INSTANCE);
+ return this;
+ }
+
+ /**
+ * Filter out the leader instances (see {@link InstanceDescription#isLeader()}.
+ *
+ * @return {@code this}
+ */
+ @Nonnull
+ public InstanceCollection isNotLeader() {
+ filters.add(NOT_LEADER_INSTANCE);
+ return this;
+ }
+
+ /**
+ * Keep only the instances that are contained in the same {@code ClusterView} cluster view
+ * as the one provided.<p>
+ *
+ * The comparison between cluster views is done on the basis of the cluster
+ * view identifier. Two cluster views with the same identifier are considered equal.<p>
+ *
+ * @param clusterView the cluster view used to filter the instances
+ * @return {@code this}
+ */
+ @Nonnull
+ public InstanceCollection isInClusterView(@Nullable ClusterView clusterView) {
+ if (clusterView != null) {
+ filters.add(new InClusterView(clusterView));
+ }
+ return this;
+ }
+
+ /**
+ * Filter out the instances that are contained in the same {@code ClusterView} cluster view
+ * as the one provided.<p>
+ *
+ * The comparison between cluster views is done on the basis of the cluster
+ * view identifier. Two cluster views with the same identifier are considered equal.<p>
+ *
+ * @param clusterView the cluster view used to filter the instances
+ * @return {@code this}
+ */
+ @Nonnull
+ public InstanceCollection isNotInClusterView(@Nullable ClusterView clusterView) {
+ if (clusterView != null) {
+ filters.add(new NotFilter(new InClusterView(clusterView)));
+ }
+ return this;
+ }
+
+ /**
+ * Return the collection of {@code InstanceDescription} instances that have not been filtered out.
+ *
+ * @return the filtered collection of instances.
+ */
+ @Nonnull
+ public Collection<InstanceDescription> get() {
+ return applyFilters();
+ }
+
+ //
+
+ /**
+ * Instances of this class can only be obtained through the {@code InstancesDiff} class.
+ * @param instances the map of instances to be filtered
+ */
+ private InstanceCollection(@Nonnull Map<String, InstanceDescription> instances) {
+ this.instances = instances;
+ }
+
+ @Nonnull
+ private Collection<InstanceDescription> applyFilters() {
+ Iterator<Map.Entry<String, InstanceDescription>> entries = instances.entrySet().iterator();
+ for ( ; entries.hasNext() ; ) {
+ Map.Entry<String, InstanceDescription> entry = entries.next();
+ for (InstanceFilter filter : filters) {
+ if (! filter.accept(entry.getValue())) {
+ entries.remove();
+ break;
+ }
+ }
+ }
+ return Collections.<InstanceDescription>unmodifiableCollection(instances.values());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/sling/discovery/commons/package-info.java b/src/main/java/org/apache/sling/discovery/commons/package-info.java
new file mode 100644
index 0000000..e104df0
--- /dev/null
+++ b/src/main/java/org/apache/sling/discovery/commons/package-info.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Provides commons utility for the Discovery API.
+ *
+ * @version 1.0.0
+ */
+@Version("1.0.0")
+package org.apache.sling.discovery.commons;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/src/test/java/org/apache/sling/discovery/commons/InstancesDiffTest.java b/src/test/java/org/apache/sling/discovery/commons/InstancesDiffTest.java
new file mode 100644
index 0000000..a2d5ddc
--- /dev/null
+++ b/src/test/java/org/apache/sling/discovery/commons/InstancesDiffTest.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.discovery.commons;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.InstanceFilter;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class InstancesDiffTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDuplicatedSlingIds() {
+ List<Instance> old = Arrays.asList(new Instance("duplicated"), new Instance("one"), new Instance("duplicated"));
+ new InstancesDiff(old, empty());
+ }
+
+ @Test
+ public void testEmptyCollections() {
+ InstancesDiff diff = new InstancesDiff(empty(), empty());
+ TestCase.assertEquals(0, diff.all(true).get().size());
+ TestCase.assertEquals(0, diff.added().get().size());
+ TestCase.assertEquals(0, diff.removed().get().size());
+ TestCase.assertEquals(0, diff.retained(true).get().size());
+ }
+
+ // added
+
+ @Test
+ public void testAddedFromEmpty() throws Exception {
+ InstancesDiff diff = new InstancesDiff(empty(), Arrays.asList(new Instance("one"), new Instance("two")));
+ TestCase.assertEquals(2, diff.added().get().size());
+ }
+
+ @Test
+ public void testAddedWithEmpty() throws Exception {
+ InstancesDiff diff = new InstancesDiff(Arrays.asList(new Instance("one"), new Instance("two")), empty());
+ TestCase.assertEquals(0, diff.added().get().size());
+ }
+
+ @Test
+ public void testAddedWithoutIntersection() throws Exception {
+ InstancesDiff diff = new InstancesDiff(Collections.singletonList(new Instance("one")),
+ Collections.singletonList(new Instance("two")));
+ TestCase.assertEquals(1, diff.added().get().size());
+ TestCase.assertEquals("two", diff.added().get().iterator().next().getSlingId());
+ }
+
+ @Test
+ public void testAddedWithIntersection() throws Exception {
+ InstancesDiff diff = new InstancesDiff(Arrays.asList(new Instance("one"), new Instance("two")),
+ Arrays.asList(new Instance("two"), new Instance("three")));
+ TestCase.assertEquals(1, diff.added().get().size());
+ TestCase.assertEquals("three", diff.added().get().iterator().next().getSlingId());
+ }
+
+ // all
+
+ @Test
+ public void testAll() throws Exception {
+ InstancesDiff diff = new InstancesDiff(Collections.singletonList(new Instance("one")),
+ Arrays.asList(new Instance("two"), new Instance("three")));
+ TestCase.assertEquals(3, diff.all(true).get().size());
+ }
+
+ @Test
+ public void testAllRetainedCollection() throws Exception {
+ Instance oldInstance = new Instance("one");
+ Instance newInstance = new Instance("one");
+ InstancesDiff diff = new InstancesDiff(
+ Collections.singletonList(oldInstance),
+ Collections.singletonList(newInstance));
+ TestCase.assertEquals(1, diff.all(true).get().size());
+ TestCase.assertEquals(newInstance, diff.all(true).get().iterator().next());
+ TestCase.assertEquals(1, diff.all(false).get().size());
+ TestCase.assertEquals(oldInstance, diff.all(false).get().iterator().next());
+ }
+
+ // removed
+
+ @Test
+ public void testRemovedFromEmpty() throws Exception {
+ InstancesDiff diff = new InstancesDiff(empty(), Arrays.asList(new Instance("one"), new Instance("two")));
+ TestCase.assertEquals(0, diff.removed().get().size());
+ }
+
+ @Test
+ public void testRemovedWithEmpty() throws Exception {
+ InstancesDiff diff = new InstancesDiff(Arrays.asList(new Instance("one"), new Instance("two")), empty());
+ TestCase.assertEquals(2, diff.removed().get().size());
+ }
+
+ @Test
+ public void testRemovedWithoutIntersection() throws Exception {
+ InstancesDiff diff = new InstancesDiff(Collections.singletonList(new Instance("one")),
+ Collections.singletonList(new Instance("two")));
+ TestCase.assertEquals(1, diff.removed().get().size());
+ TestCase.assertEquals("one", diff.removed().get().iterator().next().getSlingId());
+ }
+
+ @Test
+ public void testRemovedWithIntersection() throws Exception {
+ InstancesDiff diff = new InstancesDiff(Arrays.asList(new Instance("one"), new Instance("two")),
+ Arrays.asList(new Instance("two"), new Instance("three")));
+ TestCase.assertEquals(1, diff.removed().get().size());
+ TestCase.assertEquals("one", diff.removed().get().iterator().next().getSlingId());
+ }
+
+ // retained
+
+ @Test
+ public void testRetainedWithoutIntersection() throws Exception {
+ InstancesDiff diff = new InstancesDiff(empty(), Arrays.asList(new Instance("one"), new Instance("two")));
+ TestCase.assertEquals(0, diff.retained(true).get().size());
+ }
+
+ @Test
+ public void testRetainedWithIntersection() throws Exception {
+ InstancesDiff diff = new InstancesDiff(Arrays.asList(new Instance("one"), new Instance("two")),
+ Arrays.asList(new Instance("two"), new Instance("three")));
+ TestCase.assertEquals(1, diff.retained(true).get().size());
+ TestCase.assertEquals("two", diff.retained(true).get().iterator().next().getSlingId());
+ }
+
+ @Test
+ public void testRetainedCollection() throws Exception {
+ Instance oldInstance = new Instance("one");
+ Instance newInstance = new Instance("one");
+ InstancesDiff diff = new InstancesDiff(Collections.singletonList(oldInstance),
+ Collections.singletonList(newInstance));
+ TestCase.assertEquals(1, diff.retained(true).get().size());
+ TestCase.assertEquals(newInstance, diff.retained(true).get().iterator().next());
+ TestCase.assertEquals(oldInstance, diff.retained(false).get().iterator().next());
+ }
+
+ @Test
+ public void testRetainedByProperties() throws Exception {
+ InstancesDiff diff = new InstancesDiff(
+ Arrays.asList(new Instance("one", Collections.singletonMap("p1", "v1")), new Instance("two", Collections.singletonMap("p1", "v1"))),
+ Arrays.asList(new Instance("one", Collections.singletonMap("p1", "v2")), new Instance("two", Collections.singletonMap("p1", "v1"))));
+ TestCase.assertEquals(1, diff.retained(true, false).get().size());
+ TestCase.assertEquals("two", diff.retained(true, false).get().iterator().next().getSlingId());
+ TestCase.assertEquals(1, diff.retained(true, true).get().size());
+ TestCase.assertEquals("one", diff.retained(true, true).get().iterator().next().getSlingId());
+ }
+
+ // filters
+
+ @Test
+ public void testEmptyResult() throws Exception {
+ Collection<InstanceDescription> instances = new InstancesDiff(Arrays.asList(
+ new Instance("one"), new Instance("two")), empty()).all(true).filterWith(new InstanceFilter() {
+ public boolean accept(InstanceDescription instanceDescription) {
+ return false;
+ }
+ }).get();
+ TestCase.assertEquals(0, instances.size());
+ }
+
+ @Test
+ public void testFilterWith() throws Exception {
+ Collection<InstanceDescription> instances = new InstancesDiff(Arrays.asList(
+ new Instance("one"), new Instance("two")), empty()).all(true).filterWith(new InstanceFilter() {
+ public boolean accept(InstanceDescription instanceDescription) {
+ return "one".equals(instanceDescription.getSlingId());
+ }
+ }).get();
+ TestCase.assertEquals(1, instances.size());
+ TestCase.assertEquals("one", instances.iterator().next().getSlingId());
+ }
+
+ @Test
+ public void testIsLeader() throws Exception {
+ Collection<InstanceDescription> instances = new InstancesDiff(Arrays.asList(
+ new Instance("one", true, false, Collections.<String, String>emptyMap(), "viewId"),
+ new Instance("two", false, false, Collections.<String, String>emptyMap(), "viewId")), empty())
+ .all(true)
+ .isLeader()
+ .get();
+ TestCase.assertEquals(1, instances.size());
+ TestCase.assertEquals("one", instances.iterator().next().getSlingId());
+ }
+
+ @Test
+ public void testIsNotLeader() throws Exception {
+ Collection<InstanceDescription> instances = new InstancesDiff(Arrays.asList(
+ new Instance("one", true, false, Collections.<String, String>emptyMap(), "viewId"),
+ new Instance("two", false, false, Collections.<String, String>emptyMap(), "viewId")), empty())
+ .all(true)
+ .isNotLeader()
+ .get();
+ TestCase.assertEquals(1, instances.size());
+ TestCase.assertEquals("two", instances.iterator().next().getSlingId());
+ }
+
+ @Test
+ public void testIsLocal() throws Exception {
+ Collection<InstanceDescription> instances = new InstancesDiff(Arrays.asList(
+ new Instance("one", true, false, Collections.<String, String>emptyMap(), "viewId"),
+ new Instance("two", false, true, Collections.<String, String>emptyMap(), "viewId")), empty())
+ .all(true)
+ .isLocal()
+ .get();
+ TestCase.assertEquals(1, instances.size());
+ TestCase.assertEquals("two", instances.iterator().next().getSlingId());
+ }
+
+ @Test
+ public void testIsNotLocal() throws Exception {
+ Collection<InstanceDescription> instances = new InstancesDiff(Arrays.asList(
+ new Instance("one", true, false, Collections.<String, String>emptyMap(), "viewId"),
+ new Instance("two", false, true, Collections.<String, String>emptyMap(), "viewId")), empty())
+ .all(true)
+ .isNotLocal()
+ .get();
+ TestCase.assertEquals(1, instances.size());
+ TestCase.assertEquals("one", instances.iterator().next().getSlingId());
+ }
+
+ @Test
+ public void testIsInClusterView() throws Exception {
+ ClusterView clusterView = clusterView("viewId");
+ Collection<InstanceDescription> instances = new InstancesDiff(Arrays.asList(
+ new Instance("one", true, false, Collections.<String, String>emptyMap(), "otherView"),
+ new Instance("two", false, true, Collections.<String, String>emptyMap(), "viewId")), empty())
+ .all(true)
+ .isInClusterView(clusterView)
+ .get();
+ TestCase.assertEquals(1, instances.size());
+ TestCase.assertEquals("two", instances.iterator().next().getSlingId());
+ }
+
+ @Test
+ public void testIsNotInClusterView() throws Exception {
+ ClusterView clusterView = clusterView("yet-another-view");
+ Collection<InstanceDescription> instances = new InstancesDiff(Arrays.asList(
+ new Instance("one", true, false, Collections.<String, String>emptyMap(), "otherView"),
+ new Instance("two", false, true, Collections.<String, String>emptyMap(), "viewId")), empty())
+ .all(true)
+ .isInClusterView(clusterView)
+ .get();
+ TestCase.assertEquals(0, instances.size());
+ }
+
+ private List<Instance> empty() {
+ return Collections.<Instance>emptyList();
+ }
+
+ private class Instance implements InstanceDescription {
+
+ final String slingId;
+
+ final boolean leader;
+
+ final boolean local;
+
+ final Map<String, String> properties;
+
+ final ClusterView clusterView;
+
+ Instance(String slingId) {
+ this(slingId, false, false, Collections.<String, String>emptyMap(), "");
+ }
+
+ Instance(String slingId, Map<String, String> properties) {
+ this(slingId, false, false, properties, "");
+ }
+
+ Instance(String slingId, boolean leader, boolean local, Map<String, String> properties, String clusterViewId) {
+ this.slingId = slingId;
+ this.leader = leader;
+ this.local = local;
+ this.properties = properties;
+ clusterView = clusterView(clusterViewId);
+ }
+
+ public ClusterView getClusterView() {
+ return clusterView;
+ }
+
+ public boolean isLeader() {
+ return leader;
+ }
+
+ public boolean isLocal() {
+ return local;
+ }
+
+ public String getSlingId() {
+ return slingId;
+ }
+
+ public String getProperty(String name) {
+ return properties.get(name);
+ }
+
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+ }
+
+ private ClusterView clusterView(String clusterViewId) {
+ ClusterView clusterView = Mockito.mock(ClusterView.class);
+ Mockito.when(clusterView.getId()).thenReturn(clusterViewId);
+ return clusterView;
+ }
+}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.