blob: bd5a4f5c5e55586e75a5b7983da15cde2058f444 [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.conf;
import static java.util.Objects.requireNonNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.constraints.DefaultKeySizeConstraint;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.dataImpl.thrift.IterInfo;
import org.apache.accumulo.core.iterators.IteratorEnvironment;
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
import org.apache.accumulo.core.iterators.user.VersioningIterator;
import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class for configuring iterators. These methods were moved from IteratorUtil so that it
* could be treated as API.
*/
public class IterConfigUtil {
private static final Logger log = LoggerFactory.getLogger(IterConfigUtil.class);
public static final Comparator<IterInfo> ITER_INFO_COMPARATOR =
Comparator.comparingInt(IterInfo::getPriority);
/**
* Fetch the correct configuration key prefix for the given scope. Throws an
* IllegalArgumentException if no property exists for the given scope.
*/
public static Property getProperty(IteratorScope scope) {
requireNonNull(scope);
switch (scope) {
case scan:
return Property.TABLE_ITERATOR_SCAN_PREFIX;
case minc:
return Property.TABLE_ITERATOR_MINC_PREFIX;
case majc:
return Property.TABLE_ITERATOR_MAJC_PREFIX;
default:
throw new IllegalStateException("Could not find configuration property for IteratorScope");
}
}
/**
* Generate the initial (default) properties for a table
*
* @param limitVersion
* include a VersioningIterator at priority 20 that retains a single version of a given
* K/V pair.
* @return A map of Table properties
*/
public static Map<String,String> generateInitialTableProperties(boolean limitVersion) {
TreeMap<String,String> props = new TreeMap<>();
if (limitVersion) {
for (IteratorScope iterScope : IteratorScope.values()) {
props.put(Property.TABLE_ITERATOR_PREFIX + iterScope.name() + ".vers",
"20," + VersioningIterator.class.getName());
props.put(Property.TABLE_ITERATOR_PREFIX + iterScope.name() + ".vers.opt.maxVersions", "1");
}
}
props.put(Property.TABLE_CONSTRAINT_PREFIX + "1", DefaultKeySizeConstraint.class.getName());
return props;
}
public static List<IterInfo> parseIterConf(IteratorScope scope, List<IterInfo> iters,
Map<String,Map<String,String>> allOptions, AccumuloConfiguration conf) {
Map<String,String> properties = conf.getAllPropertiesWithPrefix(getProperty(scope));
ArrayList<IterInfo> iterators = new ArrayList<>(iters);
final Property scopeProperty = getProperty(scope);
final String scopePropertyKey = scopeProperty.getKey();
for (Entry<String,String> entry : properties.entrySet()) {
String suffix = entry.getKey().substring(scopePropertyKey.length());
String[] suffixSplit = suffix.split("\\.", 3);
if (suffixSplit.length == 1) {
String[] sa = entry.getValue().split(",");
int prio = Integer.parseInt(sa[0]);
String className = sa[1];
iterators.add(new IterInfo(prio, className, suffixSplit[0]));
} else if (suffixSplit.length == 3 && suffixSplit[1].equals("opt")) {
String iterName = suffixSplit[0];
String optName = suffixSplit[2];
allOptions.computeIfAbsent(iterName, k -> new HashMap<>()).put(optName, entry.getValue());
} else {
throw new IllegalArgumentException("Invalid iterator format: " + entry.getKey());
}
}
iterators.sort(ITER_INFO_COMPARATOR);
return iterators;
}
public static void mergeIteratorConfig(List<IterInfo> destList,
Map<String,Map<String,String>> destOpts, List<IterInfo> tableIters,
Map<String,Map<String,String>> tableOpts, List<IterInfo> ssi,
Map<String,Map<String,String>> ssio) {
destList.addAll(tableIters);
destList.addAll(ssi);
destList.sort(ITER_INFO_COMPARATOR);
Set<Entry<String,Map<String,String>>> es = tableOpts.entrySet();
for (Entry<String,Map<String,String>> entry : es) {
if (entry.getValue() == null) {
destOpts.put(entry.getKey(), null);
} else {
destOpts.put(entry.getKey(), new HashMap<>(entry.getValue()));
}
}
mergeOptions(ssio, destOpts);
}
private static void mergeOptions(Map<String,Map<String,String>> ssio,
Map<String,Map<String,String>> allOptions) {
ssio.forEach((k, v) -> {
if (v != null) {
Map<String,String> options = allOptions.get(k);
if (options == null) {
allOptions.put(k, v);
} else {
options.putAll(v);
}
}
});
}
public static IterLoad loadIterConf(IteratorScope scope, List<IterInfo> iters,
Map<String,Map<String,String>> iterOpts, AccumuloConfiguration conf) {
Map<String,Map<String,String>> allOptions = new HashMap<>();
List<IterInfo> iterators = parseIterConf(scope, iters, allOptions, conf);
mergeOptions(iterOpts, allOptions);
return new IterLoad().iters(iterators).iterOpts(allOptions);
}
/**
* Convert the list of iterators to IterInfo objects and then load the stack.
*/
public static SortedKeyValueIterator<Key,Value> convertItersAndLoad(IteratorScope scope,
SortedKeyValueIterator<Key,Value> source, AccumuloConfiguration conf,
List<IteratorSetting> iterators, IteratorEnvironment env) throws IOException {
List<IterInfo> ssiList = new ArrayList<>();
Map<String,Map<String,String>> ssio = new HashMap<>();
for (IteratorSetting is : iterators) {
ssiList.add(new IterInfo(is.getPriority(), is.getIteratorClass(), is.getName()));
ssio.put(is.getName(), is.getOptions());
}
IterLoad il = loadIterConf(scope, ssiList, ssio, conf);
il = il.iterEnv(env).useAccumuloClassLoader(true).context(conf.get(Property.TABLE_CLASSPATH));
return loadIterators(source, il);
}
/**
* Load a stack of iterators provided in the IterLoad, starting with source.
*/
public static SortedKeyValueIterator<Key,Value> loadIterators(
SortedKeyValueIterator<Key,Value> source, IterLoad iterLoad) throws IOException {
SortedKeyValueIterator<Key,Value> prev = source;
try {
for (IterInfo iterInfo : iterLoad.iters) {
Class<SortedKeyValueIterator<Key,Value>> clazz = null;
log.trace("Attempting to load iterator class {}", iterInfo.className);
if (iterLoad.classCache != null) {
clazz = iterLoad.classCache.get(iterInfo.className);
if (clazz == null) {
clazz = loadClass(iterLoad.useAccumuloClassLoader, iterLoad.context, iterInfo);
iterLoad.classCache.put(iterInfo.className, clazz);
}
} else {
clazz = loadClass(iterLoad.useAccumuloClassLoader, iterLoad.context, iterInfo);
}
SortedKeyValueIterator<Key,Value> skvi = clazz.getDeclaredConstructor().newInstance();
Map<String,String> options = iterLoad.iterOpts.get(iterInfo.iterName);
if (options == null)
options = Collections.emptyMap();
skvi.init(prev, options, iterLoad.iteratorEnvironment);
prev = skvi;
}
} catch (ReflectiveOperationException e) {
log.error(e.toString());
throw new RuntimeException(e);
}
return prev;
}
@SuppressWarnings("unchecked")
private static Class<SortedKeyValueIterator<Key,Value>> loadClass(boolean useAccumuloClassLoader,
String context, IterInfo iterInfo) throws ClassNotFoundException, IOException {
Class<SortedKeyValueIterator<Key,Value>> clazz;
if (useAccumuloClassLoader) {
if (context != null && !context.equals("")) {
clazz =
(Class<SortedKeyValueIterator<Key,Value>>) AccumuloVFSClassLoader.getContextManager()
.loadClass(context, iterInfo.className, SortedKeyValueIterator.class);
log.trace("Iterator class {} loaded from context {}, classloader: {}", iterInfo.className,
context, clazz.getClassLoader());
} else {
clazz = (Class<SortedKeyValueIterator<Key,Value>>) AccumuloVFSClassLoader
.loadClass(iterInfo.className, SortedKeyValueIterator.class);
log.trace("Iterator class {} loaded from AccumuloVFSClassLoader: {}", iterInfo.className,
clazz.getClassLoader());
}
} else {
clazz = (Class<SortedKeyValueIterator<Key,Value>>) Class.forName(iterInfo.className)
.asSubclass(SortedKeyValueIterator.class);
log.trace("Iterator class {} loaded from classpath", iterInfo.className);
}
return clazz;
}
}