001/*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020package org.apache.hadoop.hbase.client;
021
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.Map;
025import java.util.TreeMap;
026import java.util.stream.Collectors;
027import org.apache.hadoop.hbase.HConstants;
028import org.apache.hadoop.hbase.util.Bytes;
029import org.apache.hadoop.hbase.util.ClassSize;
030import org.apache.yetus.audience.InterfaceAudience;
031
032@InterfaceAudience.Public
033public abstract class OperationWithAttributes extends Operation implements Attributes {
034  // An opaque blob of attributes
035  private Map<String, byte[]> attributes;
036
037  // used for uniquely identifying an operation
038  public static final String ID_ATRIBUTE = "_operation.attributes.id";
039  private int priority = HConstants.PRIORITY_UNSET;
040
041  /**
042   * empty construction.
043   * We need this empty construction to keep binary compatibility.
044   */
045  protected OperationWithAttributes() {
046  }
047
048  protected OperationWithAttributes(OperationWithAttributes clone) {
049    this.attributes = clone.getAttributesMap() == null ? null :
050      clone.getAttributesMap().entrySet().stream()
051        .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (k, v) -> {
052          throw new RuntimeException("collisions!!!");
053        }, () -> new TreeMap<>()));
054    this.priority = clone.getPriority();
055  }
056
057  @Override
058  public OperationWithAttributes setAttribute(String name, byte[] value) {
059    if (attributes == null && value == null) {
060      return this;
061    }
062
063    if (attributes == null) {
064      attributes = new HashMap<>();
065    }
066
067    if (value == null) {
068      attributes.remove(name);
069      if (attributes.isEmpty()) {
070        this.attributes = null;
071      }
072    } else {
073      attributes.put(name, value);
074    }
075    return this;
076  }
077
078  @Override
079  public byte[] getAttribute(String name) {
080    if (attributes == null) {
081      return null;
082    }
083
084    return attributes.get(name);
085  }
086
087  @Override
088  public Map<String, byte[]> getAttributesMap() {
089    if (attributes == null) {
090      return Collections.emptyMap();
091    }
092    return Collections.unmodifiableMap(attributes);
093  }
094
095  protected long getAttributeSize() {
096    long size = 0;
097    if (attributes != null) {
098      size += ClassSize.align(this.attributes.size() * ClassSize.MAP_ENTRY);
099      for(Map.Entry<String, byte[]> entry : this.attributes.entrySet()) {
100        size += ClassSize.align(ClassSize.STRING + entry.getKey().length());
101        size += ClassSize.align(ClassSize.ARRAY + entry.getValue().length);
102      }
103    }
104    return size;
105  }
106
107  /**
108   * This method allows you to set an identifier on an operation. The original
109   * motivation for this was to allow the identifier to be used in slow query
110   * logging, but this could obviously be useful in other places. One use of
111   * this could be to put a class.method identifier in here to see where the
112   * slow query is coming from.
113   * @param id
114   *          id to set for the scan
115   */
116  public OperationWithAttributes setId(String id) {
117    setAttribute(ID_ATRIBUTE, Bytes.toBytes(id));
118    return this;
119  }
120
121  /**
122   * This method allows you to retrieve the identifier for the operation if one
123   * was set.
124   * @return the id or null if not set
125   */
126  public String getId() {
127    byte[] attr = getAttribute(ID_ATRIBUTE);
128    return attr == null? null: Bytes.toString(attr);
129  }
130
131  public OperationWithAttributes setPriority(int priority) {
132    this.priority = priority;
133    return this;
134  }
135
136  public int getPriority() {
137    return priority;
138  }
139
140}