Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 4D7A6200C17 for ; Thu, 5 Jan 2017 10:23:36 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 4C2C9160B42; Thu, 5 Jan 2017 09:23:36 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 51E8A160B4C for ; Thu, 5 Jan 2017 10:23:35 +0100 (CET) Received: (qmail 61022 invoked by uid 500); 5 Jan 2017 09:23:34 -0000 Mailing-List: contact commits-help@ignite.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ignite.apache.org Delivered-To: mailing list commits@ignite.apache.org Received: (qmail 60933 invoked by uid 99); 5 Jan 2017 09:23:34 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Jan 2017 09:23:34 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 33C24DFC3F; Thu, 5 Jan 2017 09:23:34 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: vozerov@apache.org To: commits@ignite.apache.org Date: Thu, 05 Jan 2017 09:23:37 -0000 Message-Id: In-Reply-To: <3128adad3ae64e69b405c9e4a0697c83@git.apache.org> References: <3128adad3ae64e69b405c9e4a0697c83@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [04/37] ignite git commit: IGNITE-4439 - Attribute based node filter archived-at: Thu, 05 Jan 2017 09:23:36 -0000 IGNITE-4439 - Attribute based node filter Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/6e71ef26 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/6e71ef26 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/6e71ef26 Branch: refs/heads/ignite-2.0 Commit: 6e71ef26d8e3c6f86d1f0b9f4bec97b7e33d0b2e Parents: 708cc8c Author: Valentin Kulichenko Authored: Thu Dec 22 13:05:35 2016 -0800 Committer: Valentin Kulichenko Committed: Thu Dec 22 13:13:51 2016 -0800 ---------------------------------------------------------------------- .../apache/ignite/util/AttributeNodeFilter.java | 105 +++++++++++ .../ignite/testsuites/IgniteBasicTestSuite.java | 3 + .../util/AttributeNodeFilterSelfTest.java | 184 +++++++++++++++++++ 3 files changed, 292 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/6e71ef26/modules/core/src/main/java/org/apache/ignite/util/AttributeNodeFilter.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/util/AttributeNodeFilter.java b/modules/core/src/main/java/org/apache/ignite/util/AttributeNodeFilter.java new file mode 100644 index 0000000..e2b972b --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/util/AttributeNodeFilter.java @@ -0,0 +1,105 @@ +/* + * 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.ignite.util; + +import java.util.Collections; +import java.util.Map; +import org.apache.ignite.cluster.ClusterGroup; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.lang.IgnitePredicate; +import org.apache.ignite.services.ServiceConfiguration; +import org.jetbrains.annotations.Nullable; + +/** + * Implementation of {@code IgnitePredicate} based on + * {@link IgniteConfiguration#getUserAttributes() user attributes}. + * This filter can be used in methods like {@link ClusterGroup#forPredicate(IgnitePredicate)}, + * {@link CacheConfiguration#setNodeFilter(IgnitePredicate)}, + * {@link ServiceConfiguration#setNodeFilter(IgnitePredicate)}, etc. + *

+ * The filter will evaluate to true if a node has all provided attributes set to + * corresponding values. Here is an example of how you can configure node filter for a + * cache or a service so that it's deployed only on nodes that have {@code group} + * attribute set to value {@code data}: + *

+ * <property name="nodeFilter">
+ *     <bean class="org.apache.ignite.util.ClusterAttributeNodeFilter">
+ *         <constructor-arg value="group"/>
+ *         <constructor-arg value="data"/>
+ *     </bean>
+ * </property>
+ * 
+ * You can also specify multiple attributes for the filter: + *
+ * <property name="nodeFilter">
+ *     <bean class="org.apache.ignite.util.ClusterAttributeNodeFilter">
+ *         <constructor-arg>
+ *             <map>
+ *                 <entry key="cpu-group" value="high"/>
+ *                 <entry key="memory-group" value="high"/>
+ *             </map>
+ *         </constructor-arg>
+ *     </bean>
+ * </property>
+ * 
+ * With this configuration a cache or a service will deploy only on nodes that have both + * {@code cpu-group} and {@code memory-group} attributes set to value {@code high}. + */ +public class AttributeNodeFilter implements IgnitePredicate { + /** Attributes. */ + private final Map attrs; + + /** + * Creates new node filter with a single attribute value. + * + * @param attrName Attribute name. + * @param attrVal Attribute value. + */ + public AttributeNodeFilter(String attrName, @Nullable Object attrVal) { + A.notNull(attrName, "attrName"); + + attrs = Collections.singletonMap(attrName, attrVal); + } + + /** + * Creates new node filter with a set of attributes. + * + * @param attrs Attributes. + */ + public AttributeNodeFilter(Map attrs) { + A.notNull(attrs, "attrs"); + + this.attrs = attrs; + } + + /** {@inheritDoc} */ + @Override public boolean apply(ClusterNode node) { + Map nodeAttrs = node.attributes(); + + for (Map.Entry attr : attrs.entrySet()) { + if (!F.eq(nodeAttrs.get(attr.getKey()), attr.getValue())) + return false; + } + + return true; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/6e71ef26/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java index 1c1fcf7..b2fafe2 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java @@ -20,6 +20,7 @@ package org.apache.ignite.testsuites; import java.util.Set; import junit.framework.TestSuite; import org.apache.ignite.GridSuppressedExceptionSelfTest; +import org.apache.ignite.util.AttributeNodeFilterSelfTest; import org.apache.ignite.internal.ClusterGroupHostsSelfTest; import org.apache.ignite.internal.ClusterGroupSelfTest; import org.apache.ignite.internal.GridFailFastNodeFailureDetectionSelfTest; @@ -148,6 +149,8 @@ public class IgniteBasicTestSuite extends TestSuite { suite.addTestSuite(SecurityPermissionSetBuilderTest.class); + suite.addTestSuite(AttributeNodeFilterSelfTest.class); + return suite; } } http://git-wip-us.apache.org/repos/asf/ignite/blob/6e71ef26/modules/core/src/test/java/org/apache/ignite/util/AttributeNodeFilterSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/util/AttributeNodeFilterSelfTest.java b/modules/core/src/test/java/org/apache/ignite/util/AttributeNodeFilterSelfTest.java new file mode 100644 index 0000000..ac3800f --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/util/AttributeNodeFilterSelfTest.java @@ -0,0 +1,184 @@ +/* + * 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.ignite.util; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.Map; +import org.apache.ignite.Ignite; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.lang.IgnitePredicate; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * Tests for {@link AttributeNodeFilter}. + */ +public class AttributeNodeFilterSelfTest extends GridCommonAbstractTest { + /** */ + private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** */ + private Map attrs; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + cfg.setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(IP_FINDER)); + + if (attrs != null) + cfg.setUserAttributes(attrs); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + attrs = null; + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + } + + /** + * @throws Exception If failed. + */ + public void testSingleAttribute() throws Exception { + IgnitePredicate filter = new AttributeNodeFilter("attr", "value"); + + assertTrue(filter.apply(nodeProxy(F.asMap("attr", "value")))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr", "wrong")))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr", null)))); + assertFalse(filter.apply(nodeProxy(Collections.emptyMap()))); + assertFalse(filter.apply(nodeProxy(F.asMap("wrong", "value")))); + assertFalse(filter.apply(nodeProxy(F.asMap("null", "value")))); + } + + /** + * @throws Exception If failed. + */ + public void testSingleAttributeNullValue() throws Exception { + IgnitePredicate filter = new AttributeNodeFilter("attr", null); + + assertTrue(filter.apply(nodeProxy(F.asMap("attr", null)))); + assertTrue(filter.apply(nodeProxy(Collections.emptyMap()))); + assertTrue(filter.apply(nodeProxy(F.asMap("wrong", "value")))); + assertTrue(filter.apply(nodeProxy(F.asMap("wrong", null)))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr", "value")))); + } + + /** + * @throws Exception If failed. + */ + public void testMultipleAttributes() throws Exception { + IgnitePredicate filter = + new AttributeNodeFilter(F.asMap("attr1", "value1", "attr2", "value2")); + + assertTrue(filter.apply(nodeProxy(F.asMap("attr1", "value1", "attr2", "value2")))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr1", "wrong", "attr2", "value2")))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr1", "value1", "attr2", "wrong")))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr1", "wrong", "attr2", "wrong")))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr1", "value1")))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr2", "value2")))); + assertFalse(filter.apply(nodeProxy(Collections.emptyMap()))); + } + + /** + * @throws Exception If failed. + */ + public void testMultipleAttributesNullValues() throws Exception { + IgnitePredicate filter = new AttributeNodeFilter(F.asMap("attr1", null, "attr2", null)); + + assertTrue(filter.apply(nodeProxy(F.asMap("attr1", null, "attr2", null)))); + assertTrue(filter.apply(nodeProxy(F.asMap("attr1", null)))); + assertTrue(filter.apply(nodeProxy(F.asMap("attr2", null)))); + assertTrue(filter.apply(nodeProxy(Collections.emptyMap()))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr1", "value1")))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr2", "value2")))); + assertFalse(filter.apply(nodeProxy(F.asMap("attr1", "value1", "attr2", "value2")))); + } + + /** + * @throws Exception If failed. + */ + public void testClusterGroup() throws Exception { + Ignite group1 = startGridsMultiThreaded(3); + + attrs = F.asMap("group", "data"); + + Ignite group2 = startGridsMultiThreaded(3, 2); + + assertEquals(2, group1.cluster().forPredicate(new AttributeNodeFilter("group", "data")).nodes().size()); + assertEquals(2, group2.cluster().forPredicate(new AttributeNodeFilter("group", "data")).nodes().size()); + + assertEquals(3, group1.cluster().forPredicate(new AttributeNodeFilter("group", null)).nodes().size()); + assertEquals(3, group2.cluster().forPredicate(new AttributeNodeFilter("group", null)).nodes().size()); + + assertEquals(0, group1.cluster().forPredicate(new AttributeNodeFilter("group", "wrong")).nodes().size()); + assertEquals(0, group2.cluster().forPredicate(new AttributeNodeFilter("group", "wrong")).nodes().size()); + } + + /** + * @throws Exception If failed. + */ + public void testCacheFilter() throws Exception { + Ignite group1 = startGridsMultiThreaded(3); + + attrs = F.asMap("group", "data"); + + Ignite group2 = startGridsMultiThreaded(3, 2); + + group1.createCache(new CacheConfiguration<>("test-cache"). + setNodeFilter(new AttributeNodeFilter("group", "data"))); + + assertEquals(2, group1.cluster().forDataNodes("test-cache").nodes().size()); + assertEquals(2, group2.cluster().forDataNodes("test-cache").nodes().size()); + + assertEquals(0, group1.cluster().forDataNodes("wrong").nodes().size()); + assertEquals(0, group2.cluster().forDataNodes("wrong").nodes().size()); + } + + /** + * @param attrs Attributes. + * @return Node proxy. + */ + private static ClusterNode nodeProxy(final Map attrs) { + return (ClusterNode)Proxy.newProxyInstance( + ClusterNode.class.getClassLoader(), + new Class[] { ClusterNode.class }, + new InvocationHandler() { + @SuppressWarnings("SuspiciousMethodCalls") + @Override public Object invoke(Object proxy, Method mtd, Object[] args) throws Throwable { + if ("attributes".equals(mtd.getName())) + return attrs; + + throw new UnsupportedOperationException(); + } + }); + } +}