| /* |
| * 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.client.summary; |
| |
| import static java.nio.charset.StandardCharsets.UTF_8; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import org.apache.accumulo.core.summary.SummarizerConfigurationUtil; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.hash.Hasher; |
| import com.google.common.hash.Hashing; |
| |
| /** |
| * This class encapsulates the configuration needed to instantiate a {@link Summarizer}. It also |
| * provides methods and documentation for setting the table properties that configure a Summarizer. |
| * |
| * @since 2.0.0 |
| */ |
| public class SummarizerConfiguration { |
| |
| private final String className; |
| private final Map<String,String> options; |
| private int hashCode = 0; |
| private final String configId; |
| |
| private SummarizerConfiguration(String className, String configId, Map<String,String> options) { |
| this.className = className; |
| this.options = ImmutableMap.copyOf(options); |
| |
| if (configId == null) { |
| ArrayList<String> keys = new ArrayList<>(this.options.keySet()); |
| Collections.sort(keys); |
| Hasher hasher = Hashing.murmur3_32().newHasher(); |
| hasher.putString(className, UTF_8); |
| for (String key : keys) { |
| hasher.putString(key, UTF_8); |
| hasher.putString(options.get(key), UTF_8); |
| } |
| |
| this.configId = hasher.hash().toString(); |
| } else { |
| this.configId = configId; |
| } |
| } |
| |
| /** |
| * @return the name of a class that implements @link {@link Summarizer}. |
| */ |
| public String getClassName() { |
| return className; |
| } |
| |
| /** |
| * @return custom options for a {link @Summarizer} |
| */ |
| public Map<String,String> getOptions() { |
| return options; |
| } |
| |
| /** |
| * The propertyId is used to when creating table properties for a summarizer. Its not used for |
| * equality or hashCode for this class. |
| */ |
| public String getPropertyId() { |
| return configId; |
| } |
| |
| @Override |
| public String toString() { |
| return className + " " + configId + " " + options; |
| } |
| |
| /** |
| * Compares the classname and options to determine equality. |
| */ |
| @Override |
| public boolean equals(Object o) { |
| if (o instanceof SummarizerConfiguration) { |
| SummarizerConfiguration osc = (SummarizerConfiguration) o; |
| return className.equals(osc.className) && options.equals(osc.options); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Hashes the classname and options to create a hashcode. |
| */ |
| @Override |
| public int hashCode() { |
| if (hashCode == 0) { |
| hashCode = 31 * options.hashCode() + className.hashCode(); |
| } |
| return hashCode; |
| } |
| |
| /** |
| * Converts this configuration to Accumulo per table properties. The returned map has the |
| * following key values. The {@code <configId>} below is from {@link #getPropertyId()}. The |
| * {@code <optionKey>} and {@code <optionValue>} below are derived from the key values of |
| * {@link #getOptions()}. |
| * |
| * <pre> |
| * {@code |
| * table.summarizer.<configId>=<classname> |
| * table.summarizer.<configId>.opt.<optionKey1>=<optionValue1> |
| * table.summarizer.<configId>.opt.<optionKey2>=<optionValue2> |
| * . |
| * . |
| * . |
| * table.summarizer.<configId>.opt.<optionKeyN>=<optionValueN> |
| * } |
| * </pre> |
| */ |
| public Map<String,String> toTableProperties() { |
| return SummarizerConfigurationUtil.toTablePropertiesMap(Collections.singletonList(this)); |
| } |
| |
| /** |
| * Encodes each configuration in the same way as {@link #toTableProperties()}. |
| * |
| * @throws IllegalArgumentException |
| * when there are duplicate values for {@link #getPropertyId()} |
| */ |
| public static Map<String,String> toTableProperties(SummarizerConfiguration... configurations) { |
| return SummarizerConfigurationUtil.toTablePropertiesMap(Arrays.asList(configurations)); |
| } |
| |
| /** |
| * Encodes each configuration in the same way as {@link #toTableProperties()}. |
| * |
| * @throws IllegalArgumentException |
| * when there are duplicate values for {@link #getPropertyId()} |
| */ |
| public static Map<String,String> toTableProperties( |
| Collection<SummarizerConfiguration> configurations) { |
| return SummarizerConfigurationUtil.toTablePropertiesMap(new ArrayList<>(configurations)); |
| } |
| |
| /** |
| * Decodes table properties with the prefix {@code table.summarizer} into |
| * {@link SummarizerConfiguration} objects. Table properties with prefixes other than |
| * {@code table.summarizer} are ignored. |
| */ |
| public static Collection<SummarizerConfiguration> fromTableProperties(Map<String,String> props) { |
| return fromTableProperties(props.entrySet()); |
| } |
| |
| /** |
| * @see #fromTableProperties(Map) |
| */ |
| public static Collection<SummarizerConfiguration> fromTableProperties( |
| Iterable<Entry<String,String>> props) { |
| return SummarizerConfigurationUtil.getSummarizerConfigs(props); |
| } |
| |
| public static class Builder { |
| private String className; |
| private ImmutableMap.Builder<String,String> imBuilder; |
| private String configId = null; |
| |
| private Builder(String className) { |
| this.className = className; |
| this.imBuilder = ImmutableMap.builder(); |
| } |
| |
| /** |
| * Sets the id used when generating table properties. Setting this is optional. If not set, an |
| * id is generated using hashing that will likely be unique. |
| * |
| * @param propId |
| * This id is used when converting a {@link SummarizerConfiguration} to table |
| * properties. Since tables can have multiple summarizers, make sure its unique. |
| * |
| * @see SummarizerConfiguration#toTableProperties() |
| */ |
| public Builder setPropertyId(String propId) { |
| Preconditions.checkArgument(propId.matches("\\w+"), "Config Id %s is not alphanum", propId); |
| this.configId = propId; |
| return this; |
| } |
| |
| /** |
| * Adds an option that Summarizers can use when constructing Collectors and Combiners. |
| * |
| * @return this |
| * |
| * @see SummarizerConfiguration#getOptions() |
| */ |
| public Builder addOption(String key, String value) { |
| Preconditions.checkArgument(key.matches("\\w+"), "Option Id %s is not alphanum", key); |
| imBuilder.put(key, value); |
| return this; |
| } |
| |
| /** |
| * Adds an option that Summarizers can use when constructing Collectors and Combiners. |
| * |
| * @return this |
| * |
| * @see SummarizerConfiguration#getOptions() |
| */ |
| public Builder addOption(String key, long value) { |
| return addOption(key, Long.toString(value)); |
| } |
| |
| /** |
| * Convenience method for adding multiple options. The following |
| * |
| * <pre> |
| * {@code builder.addOptions("opt1","val1","opt2","val2","opt3","val3")} |
| * </pre> |
| * |
| * <p> |
| * is equivalent to |
| * |
| * <pre> |
| * {@code |
| * builder.addOption("opt1","val1"); |
| * builder.addOption("opt2","val2"); |
| * builder.addOption("opt3","val3"); |
| * } |
| * </pre> |
| * |
| * @param keyValuePairs |
| * This array must have an even and positive number of elements. |
| * @return this |
| * @see SummarizerConfiguration#getOptions() |
| */ |
| public Builder addOptions(String... keyValuePairs) { |
| Preconditions.checkArgument(keyValuePairs.length % 2 == 0 && keyValuePairs.length > 0, |
| "Require an even, positive number of arguments, got %s", keyValuePairs.length); |
| for (int i = 0; i < keyValuePairs.length; i += 2) { |
| addOption(keyValuePairs[i], keyValuePairs[i + 1]); |
| } |
| return this; |
| } |
| |
| /** |
| * @param options |
| * Each entry in the map is passed to {@link #addOption(String, String)} |
| * @return this |
| * |
| * @see SummarizerConfiguration#getOptions() |
| */ |
| public Builder addOptions(Map<String,String> options) { |
| options.entrySet().forEach(e -> addOption(e.getKey(), e.getValue())); |
| return this; |
| } |
| |
| public SummarizerConfiguration build() { |
| return new SummarizerConfiguration(className, configId, imBuilder.build()); |
| } |
| } |
| |
| /** |
| * Call this method to initiate a chain of fluent method calls to a create an immutable |
| * {@link SummarizerConfiguration} |
| * |
| * @param className |
| * The fully qualified name of a class that implements {@link Summarizer}. |
| */ |
| public static Builder builder(String className) { |
| return new Builder(className); |
| } |
| |
| /** |
| * @see #builder(String) |
| */ |
| public static Builder builder(Class<? extends Summarizer> clazz) { |
| return new Builder(clazz.getName()); |
| } |
| } |