blob: 57940095ed5e802fbf65150306baa6ea5a51761b [file] [log] [blame]
/*
* 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.accumulo.core.iterators;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.accumulo.core.conf.ConfigurationCopy;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.ByteSequence;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.data.impl.KeyExtent;
import org.apache.accumulo.core.data.thrift.IterInfo;
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
import org.apache.accumulo.core.iterators.system.MultiIteratorTest;
import org.apache.accumulo.core.iterators.user.AgeOffFilter;
import org.apache.accumulo.core.iterators.user.SummingCombiner;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class IteratorUtilTest {
private static final Logger log = LoggerFactory.getLogger(IteratorUtilTest.class);
private static final Collection<ByteSequence> EMPTY_COL_FAMS = new ArrayList<>();
static class WrappedIter implements SortedKeyValueIterator<Key,Value> {
protected SortedKeyValueIterator<Key,Value> source;
@Override
public WrappedIter deepCopy(IteratorEnvironment env) {
throw new UnsupportedOperationException();
}
@Override
public Key getTopKey() {
return source.getTopKey();
}
@Override
public Value getTopValue() {
return source.getTopValue();
}
@Override
public boolean hasTop() {
return source.hasTop();
}
@Override
public void init(SortedKeyValueIterator<Key,Value> source, Map<String,String> options,
IteratorEnvironment env) throws IOException {
this.source = source;
}
@Override
public void next() throws IOException {
source.next();
}
@Override
public void seek(Range range, Collection<ByteSequence> columnFamilies, boolean inclusive)
throws IOException {
source.seek(range, columnFamilies, inclusive);
}
}
static class AddingIter extends WrappedIter {
int amount = 1;
@Override
public Value getTopValue() {
Value val = super.getTopValue();
int orig = Integer.parseInt(val.toString());
return new Value(((orig + amount) + "").getBytes());
}
@Override
public void init(SortedKeyValueIterator<Key,Value> source, Map<String,String> options,
IteratorEnvironment env) throws IOException {
super.init(source, options, env);
String amount = options.get("amount");
if (amount != null) {
this.amount = Integer.parseInt(amount);
}
}
}
static class SquaringIter extends WrappedIter {
@Override
public Value getTopValue() {
Value val = super.getTopValue();
int orig = Integer.parseInt(val.toString());
return new Value(((orig * orig) + "").getBytes());
}
}
@Test
public void test1() throws IOException {
ConfigurationCopy conf = new ConfigurationCopy();
// create an iterator that adds 1 and then squares
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".addIter",
"1," + AddingIter.class.getName());
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".sqIter",
"2," + SquaringIter.class.getName());
TreeMap<Key,Value> tm = new TreeMap<>();
MultiIteratorTest.newKeyValue(tm, 1, 0, false, "1");
MultiIteratorTest.newKeyValue(tm, 2, 0, false, "2");
SortedMapIterator source = new SortedMapIterator(tm);
SortedKeyValueIterator<Key,Value> iter = IteratorUtil.loadIterators(IteratorScope.minc, source,
new KeyExtent("tab", null, null), conf, new DefaultIteratorEnvironment(conf));
iter.seek(new Range(), EMPTY_COL_FAMS, false);
assertTrue(iter.hasTop());
assertTrue(iter.getTopKey().equals(MultiIteratorTest.newKey(1, 0)));
assertTrue(iter.getTopValue().toString().equals("4"));
iter.next();
assertTrue(iter.hasTop());
assertTrue(iter.getTopKey().equals(MultiIteratorTest.newKey(2, 0)));
assertTrue(iter.getTopValue().toString().equals("9"));
iter.next();
assertFalse(iter.hasTop());
}
@Test
public void test4() throws IOException {
// try loading for a different scope
AccumuloConfiguration conf = new ConfigurationCopy();
TreeMap<Key,Value> tm = new TreeMap<>();
MultiIteratorTest.newKeyValue(tm, 1, 0, false, "1");
MultiIteratorTest.newKeyValue(tm, 2, 0, false, "2");
SortedMapIterator source = new SortedMapIterator(tm);
SortedKeyValueIterator<Key,Value> iter = IteratorUtil.loadIterators(IteratorScope.majc, source,
new KeyExtent("tab", null, null), conf, new DefaultIteratorEnvironment(conf));
iter.seek(new Range(), EMPTY_COL_FAMS, false);
assertTrue(iter.hasTop());
assertTrue(iter.getTopKey().equals(MultiIteratorTest.newKey(1, 0)));
assertTrue(iter.getTopValue().toString().equals("1"));
iter.next();
assertTrue(iter.hasTop());
assertTrue(iter.getTopKey().equals(MultiIteratorTest.newKey(2, 0)));
assertTrue(iter.getTopValue().toString().equals("2"));
iter.next();
assertFalse(iter.hasTop());
}
@Test
public void test3() throws IOException {
// change the load order, so it squares and then adds
ConfigurationCopy conf = new ConfigurationCopy();
TreeMap<Key,Value> tm = new TreeMap<>();
MultiIteratorTest.newKeyValue(tm, 1, 0, false, "1");
MultiIteratorTest.newKeyValue(tm, 2, 0, false, "2");
SortedMapIterator source = new SortedMapIterator(tm);
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".addIter",
"2," + AddingIter.class.getName());
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".sqIter",
"1," + SquaringIter.class.getName());
SortedKeyValueIterator<Key,Value> iter = IteratorUtil.loadIterators(IteratorScope.minc, source,
new KeyExtent("tab", null, null), conf, new DefaultIteratorEnvironment(conf));
iter.seek(new Range(), EMPTY_COL_FAMS, false);
assertTrue(iter.hasTop());
assertTrue(iter.getTopKey().equals(MultiIteratorTest.newKey(1, 0)));
assertTrue(iter.getTopValue().toString().equals("2"));
iter.next();
assertTrue(iter.hasTop());
assertTrue(iter.getTopKey().equals(MultiIteratorTest.newKey(2, 0)));
assertTrue(iter.getTopValue().toString().equals("5"));
iter.next();
assertFalse(iter.hasTop());
}
@Test
public void test2() throws IOException {
ConfigurationCopy conf = new ConfigurationCopy();
// create an iterator that adds 1 and then squares
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".addIter",
"1," + AddingIter.class.getName());
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".addIter.opt.amount",
"7");
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".sqIter",
"2," + SquaringIter.class.getName());
TreeMap<Key,Value> tm = new TreeMap<>();
MultiIteratorTest.newKeyValue(tm, 1, 0, false, "1");
MultiIteratorTest.newKeyValue(tm, 2, 0, false, "2");
SortedMapIterator source = new SortedMapIterator(tm);
SortedKeyValueIterator<Key,Value> iter = IteratorUtil.loadIterators(IteratorScope.minc, source,
new KeyExtent("tab", null, null), conf, new DefaultIteratorEnvironment(conf));
iter.seek(new Range(), EMPTY_COL_FAMS, false);
assertTrue(iter.hasTop());
assertTrue(iter.getTopKey().equals(MultiIteratorTest.newKey(1, 0)));
assertTrue(iter.getTopValue().toString().equals("64"));
iter.next();
assertTrue(iter.hasTop());
assertTrue(iter.getTopKey().equals(MultiIteratorTest.newKey(2, 0)));
assertTrue(iter.getTopValue().toString().equals("81"));
iter.next();
assertFalse(iter.hasTop());
}
@Test
public void test5() throws IOException {
ConfigurationCopy conf = new ConfigurationCopy();
// create an iterator that ages off
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".filter",
"1," + AgeOffFilter.class.getName());
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".filter.opt.ttl", "100");
conf.set(Property.TABLE_ITERATOR_PREFIX + IteratorScope.minc.name() + ".filter.opt.currentTime",
"1000");
TreeMap<Key,Value> tm = new TreeMap<>();
MultiIteratorTest.newKeyValue(tm, 1, 850, false, "1");
MultiIteratorTest.newKeyValue(tm, 2, 950, false, "2");
SortedMapIterator source = new SortedMapIterator(tm);
SortedKeyValueIterator<Key,Value> iter = IteratorUtil.loadIterators(IteratorScope.minc, source,
new KeyExtent("tab", null, null), conf, new DefaultIteratorEnvironment(conf));
iter.seek(new Range(), EMPTY_COL_FAMS, false);
assertTrue(iter.hasTop());
assertTrue(iter.getTopKey().equals(MultiIteratorTest.newKey(2, 950)));
iter.next();
assertFalse(iter.hasTop());
}
@Test
public void onlyReadsRelevantIteratorScopeConfigurations() throws Exception {
Map<String,String> data = new HashMap<>();
// Make some configuration items, one with a bogus scope
data.put(Property.TABLE_ITERATOR_SCAN_PREFIX + "foo", "50," + SummingCombiner.class.getName());
data.put(Property.TABLE_ITERATOR_SCAN_PREFIX + "foo.opt." + SummingCombiner.ALL_OPTION, "true");
data.put(Property.TABLE_ITERATOR_PREFIX + ".fakescope.bar",
"50," + SummingCombiner.class.getName());
data.put(Property.TABLE_ITERATOR_SCAN_PREFIX + "foo.opt.fakeopt", "fakevalue");
AccumuloConfiguration conf = new ConfigurationCopy(data);
List<IterInfo> iterators = new ArrayList<>();
Map<String,Map<String,String>> options = new HashMap<>();
IteratorUtil.parseIterConf(IteratorScope.scan, iterators, options, conf);
assertEquals(1, iterators.size());
IterInfo ii = iterators.get(0);
assertEquals(new IterInfo(50, SummingCombiner.class.getName(), "foo"), ii);
}
/**
* Iterators should not contain dots in the name. Also, if the split size on "." is greater than
* one, it should be 3, i.e., itername.opt.optname
*/
@Test
public void testInvalidIteratorFormats() {
Map<String,String> data = new HashMap<>();
List<IterInfo> iterators = new ArrayList<>();
Map<String,Map<String,String>> options = new HashMap<>();
AccumuloConfiguration conf;
// create iterator with 'dot' in name
try {
data.put(Property.TABLE_ITERATOR_SCAN_PREFIX + "foo.bar",
"50," + SummingCombiner.class.getName());
conf = new ConfigurationCopy(data);
IteratorUtil.parseIterConf(IteratorScope.scan, iterators, options, conf);
} catch (IllegalArgumentException ex) {
log.debug("caught expected exception: " + ex.getMessage());
}
data.clear();
iterators.clear();
options.clear();
// create iterator with 'dot' in name and with split size of 3. If split size of three, then
// second part must be 'opt'.
try {
data.put(Property.TABLE_ITERATOR_SCAN_PREFIX + "foo.bar.baz",
"49," + SummingCombiner.class.getName());
conf = new ConfigurationCopy(data);
IteratorUtil.parseIterConf(IteratorScope.scan, iterators, options, conf);
} catch (IllegalArgumentException ex) {
log.debug("caught expected exception: " + ex.getMessage());
}
data.clear();
iterators.clear();
options.clear();
// create iterator with invalid option format
try {
data.put(Property.TABLE_ITERATOR_SCAN_PREFIX + "foobar",
"48," + SummingCombiner.class.getName());
data.put(Property.TABLE_ITERATOR_SCAN_PREFIX + "foobar.opt", "fakevalue");
conf = new ConfigurationCopy(data);
IteratorUtil.parseIterConf(IteratorScope.scan, iterators, options, conf);
assertEquals(1, iterators.size());
IterInfo ii = iterators.get(0);
assertEquals(new IterInfo(48, SummingCombiner.class.getName(), "foobar"), ii);
} catch (IllegalArgumentException ex) {
log.debug("caught expected exception: " + ex.getMessage());
}
data.clear();
iterators.clear();
options.clear();
// create iterator with 'opt' in incorrect position
try {
data.put(Property.TABLE_ITERATOR_SCAN_PREFIX + "foobaz",
"47," + SummingCombiner.class.getName());
data.put(Property.TABLE_ITERATOR_SCAN_PREFIX + "foobaz.fake.opt", "fakevalue");
conf = new ConfigurationCopy(data);
IteratorUtil.parseIterConf(IteratorScope.scan, iterators, options, conf);
assertEquals(1, iterators.size());
IterInfo ii = iterators.get(0);
assertEquals(new IterInfo(47, SummingCombiner.class.getName(), "foobaz"), ii);
} catch (IllegalArgumentException ex) {
log.debug("caught expected exception: " + ex.getMessage());
}
}
}