/* * 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.internal.processors.cache.query.continuous; import java.io.Serializable; import java.util.Collections; import javax.cache.configuration.Factory; import javax.cache.event.CacheEntryEvent; import javax.cache.event.CacheEntryEventFilter; import javax.cache.event.CacheEntryUpdatedListener; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; import org.apache.ignite.IgniteException; import org.apache.ignite.cache.query.ContinuousQuery; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; 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.GridStringLogger; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; /** */ @SuppressWarnings("unused") public class GridCacheContinuousQueryNodesFilteringTest extends GridCommonAbstractTest implements Serializable { /** */ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); /** */ private static final String ENTRY_FILTER_CLS_NAME = "org.apache.ignite.tests.p2p.CacheDeploymentEntryEventFilter"; /** * Tests that node not matched by filter really does not try to participate in the query. * * @throws Exception if failed. */ @SuppressWarnings("EmptyTryBlock") public void testNodeWithoutAttributeExclusion() throws Exception { try (Ignite node1 = startNodeWithCache()) { try (Ignite node2 = startGrid("node2", getConfiguration("node2", false, null))) { assertEquals(2, node2.cluster().nodes().size()); } } } /** * Test that node matched by filter and having filter instantiation problems fails for sure. * * @throws Exception if failed. */ public void testNodeWithAttributeFailure() throws Exception { try (Ignite node1 = startNodeWithCache()) { GridStringLogger log = new GridStringLogger(); try (Ignite node2 = startGrid("node2", getConfiguration("node2", true, log))) { fail(); } catch (IgniteException ignored) { assertTrue(log.toString().contains("Class not found for continuous query remote filter " + "[name=org.apache.ignite.tests.p2p.CacheDeploymentEntryEventFilter]")); } } } /** * Start first, attribute-bearing, node, create new cache and launch continuous query on it. * * @return Node. * @throws Exception if failed. */ private Ignite startNodeWithCache() throws Exception { Ignite node1 = startGrid("node1", getConfiguration("node1", true, null)); CacheConfiguration<Integer, Integer> ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME); ccfg.setName("attrsTestCache"); ccfg.setNodeFilter(new IgnitePredicate<ClusterNode>() { /** {@inheritDoc} */ @Override public boolean apply(ClusterNode node) { return "data".equals(node.attribute("node-type")); } }); IgniteCache<Integer, Integer> cache = node1.createCache(ccfg); ContinuousQuery<Integer, Integer> qry = new ContinuousQuery<>(); qry.setRemoteFilterFactory(new RemoteFilterFactory()); qry.setLocalListener(new CacheEntryUpdatedListener<Integer, Integer>() { /** {@inheritDoc} */ @Override public void onUpdated(Iterable<CacheEntryEvent<? extends Integer, ? extends Integer>> evts) { // No-op. } }); RemoteFilterFactory.clsLdr = getExternalClassLoader(); cache.query(qry); // Switch class loader before starting the second node. RemoteFilterFactory.clsLdr = getClass().getClassLoader(); return node1; } /** * @param name Node name. * @param setAttr Flag indicating whether node user attribute should be set. * @param log Logger. * @return Node configuration w/specified name. * @throws Exception If failed. */ private IgniteConfiguration getConfiguration(String name, boolean setAttr, GridStringLogger log) throws Exception { IgniteConfiguration cfg = optimize(getConfiguration(name)); ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER); if (setAttr) cfg.setUserAttributes(Collections.singletonMap("node-type", "data")); cfg.setGridLogger(log); return cfg; } /** * */ private static class RemoteFilterFactory implements Factory<CacheEntryEventFilter<Integer, Integer>> { /** */ private static ClassLoader clsLdr; /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public CacheEntryEventFilter<Integer, Integer> create() { try { Class<?> filterCls = clsLdr.loadClass(ENTRY_FILTER_CLS_NAME); assert CacheEntryEventFilter.class.isAssignableFrom(filterCls); return ((Class<CacheEntryEventFilter>)filterCls).newInstance(); } catch (ClassNotFoundException e) { throw new IgniteException("Class not found for continuous query remote filter [name=" + e.getMessage() + "]"); } catch (Exception e) { // We really don't expect anything else fancy here. throw new AssertionError("Unexpected exception", e); } } } }