| /* |
| * 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.druid.server.lookup; |
| |
| import com.fasterxml.jackson.annotation.JsonTypeName; |
| import com.google.common.base.Function; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Sets; |
| import org.apache.druid.common.config.NullHandling; |
| import org.apache.druid.java.util.common.ISE; |
| import org.apache.druid.query.lookup.LookupExtractor; |
| import org.apache.druid.server.lookup.cache.polling.OffHeapPollingCache; |
| import org.apache.druid.server.lookup.cache.polling.OnHeapPollingCache; |
| import org.apache.druid.server.lookup.cache.polling.PollingCacheFactory; |
| import org.apache.druid.testing.InitializedNullHandlingTest; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.ExpectedException; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| import javax.annotation.Nullable; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| @RunWith(Parameterized.class) |
| public class PollingLookupTest extends InitializedNullHandlingTest |
| { |
| private static final Map<String, String> FIRST_LOOKUP_MAP = ImmutableMap.of( |
| "foo", "bar", |
| "bad", "bar", |
| "how about that", "foo", |
| "empty string", "" |
| ); |
| |
| private static final Map<String, String> SECOND_LOOKUP_MAP = ImmutableMap.of( |
| "new-foo", "new-bar", |
| "new-bad", "new-bar" |
| ); |
| |
| private static final long POLL_PERIOD = 1000L; |
| |
| @Rule |
| public ExpectedException expectedException = ExpectedException.none(); |
| |
| @JsonTypeName("mock") |
| private static class MockDataFetcher implements DataFetcher |
| { |
| private int callNumber = 0; |
| |
| @Override |
| public Iterable fetchAll() |
| { |
| if (callNumber == 0) { |
| callNumber++; |
| return FIRST_LOOKUP_MAP.entrySet(); |
| } |
| return SECOND_LOOKUP_MAP.entrySet(); |
| } |
| |
| @Nullable |
| @Override |
| public Object fetch(Object key) |
| { |
| return null; |
| } |
| |
| @Override |
| public Iterable fetch(Iterable keys) |
| { |
| return null; |
| } |
| |
| @Override |
| public List reverseFetchKeys(Object value) |
| { |
| return null; |
| } |
| |
| @Override |
| public boolean equals(Object obj) |
| { |
| return obj instanceof MockDataFetcher; |
| } |
| } |
| |
| @Parameterized.Parameters |
| public static Collection<Object[]> inputData() |
| { |
| return Arrays.asList(new Object[][]{ |
| {new OffHeapPollingCache.OffHeapPollingCacheProvider()}, |
| {new OnHeapPollingCache.OnHeapPollingCacheProvider<>()} |
| }); |
| } |
| |
| private final PollingCacheFactory pollingCacheFactory; |
| private final DataFetcher dataFetcher = new MockDataFetcher(); |
| private PollingLookup pollingLookup; |
| |
| public PollingLookupTest(PollingCacheFactory pollingCacheFactory) |
| { |
| this.pollingCacheFactory = pollingCacheFactory; |
| } |
| |
| @Before |
| public void setUp() |
| { |
| pollingLookup = new PollingLookup(POLL_PERIOD, dataFetcher, pollingCacheFactory); |
| } |
| |
| @After |
| public void tearDown() |
| { |
| if (pollingLookup != null) { |
| pollingLookup.close(); |
| } |
| pollingLookup = null; |
| } |
| |
| @Test(expected = ISE.class) |
| public void testClose() |
| { |
| pollingLookup.close(); |
| pollingLookup.apply("key"); |
| } |
| |
| @Test |
| public void testApply() |
| { |
| assertMapLookup(FIRST_LOOKUP_MAP, pollingLookup); |
| } |
| |
| @Test(timeout = POLL_PERIOD * 3) |
| public void testApplyAfterDataChange() throws InterruptedException |
| { |
| assertMapLookup(FIRST_LOOKUP_MAP, pollingLookup); |
| Thread.sleep(POLL_PERIOD * 2); |
| assertMapLookup(SECOND_LOOKUP_MAP, pollingLookup); |
| } |
| |
| @Test |
| public void testUnapply() |
| { |
| Assert.assertEquals( |
| "reverse lookup should match", |
| Sets.newHashSet("foo", "bad"), |
| Sets.newHashSet(pollingLookup.unapply("bar")) |
| ); |
| Assert.assertEquals( |
| "reverse lookup should match", |
| Sets.newHashSet("how about that"), |
| Sets.newHashSet(pollingLookup.unapply("foo")) |
| ); |
| Assert.assertEquals( |
| "reverse lookup should match", |
| Sets.newHashSet("empty string"), |
| Sets.newHashSet(pollingLookup.unapply("")) |
| ); |
| Assert.assertEquals( |
| "reverse lookup of none existing value should be empty list", |
| Collections.emptyList(), |
| pollingLookup.unapply("does't exist") |
| ); |
| } |
| |
| @Test |
| public void testBulkApply() |
| { |
| Map<String, String> map = pollingLookup.applyAll(FIRST_LOOKUP_MAP.keySet()); |
| Assert.assertEquals(FIRST_LOOKUP_MAP, Maps.transformValues(map, new Function<String, String>() |
| { |
| @Override |
| public String apply(String input) |
| { |
| //make sure to rewrite null strings as empty. |
| return NullHandling.nullToEmptyIfNeeded(input); |
| } |
| })); |
| } |
| |
| @Test |
| public void testGetCacheKey() |
| { |
| PollingLookup pollingLookup2 = new PollingLookup(1L, dataFetcher, pollingCacheFactory); |
| Assert.assertFalse(Arrays.equals(pollingLookup2.getCacheKey(), pollingLookup.getCacheKey())); |
| } |
| |
| @Test |
| public void testCanGetKeySet() |
| { |
| Assert.assertFalse(pollingLookup.canGetKeySet()); |
| } |
| |
| @Test |
| public void testKeySet() |
| { |
| expectedException.expect(UnsupportedOperationException.class); |
| pollingLookup.keySet(); |
| } |
| |
| private void assertMapLookup(Map<String, String> map, LookupExtractor lookup) |
| { |
| for (Map.Entry<String, String> entry : map.entrySet()) { |
| String key = entry.getKey(); |
| String val = entry.getValue(); |
| Assert.assertEquals("non-null check", NullHandling.emptyToNullIfNeeded(val), lookup.apply(key)); |
| } |
| } |
| } |