001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to you under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.hadoop.hbase.quotas;
018
019import java.util.Objects;
020import java.util.Optional;
021import org.apache.commons.lang3.builder.HashCodeBuilder;
022import org.apache.yetus.audience.InterfaceAudience;
023import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
024import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos;
025import org.apache.hadoop.util.StringUtils;
026
027/**
028 * A point-in-time view of a space quota on a table.
029 */
030@InterfaceAudience.Private
031public class SpaceQuotaSnapshot implements SpaceQuotaSnapshotView {
032  private static final SpaceQuotaSnapshot NO_SUCH_SNAPSHOT = new SpaceQuotaSnapshot(
033      SpaceQuotaStatus.notInViolation(), 0, Long.MAX_VALUE);
034  private final SpaceQuotaStatus quotaStatus;
035  private final long usage;
036  private final long limit;
037
038  /**
039   * Encapsulates the state of a quota on a table. The quota may or may not be in violation.
040   * If the quota is not in violation, the violation may be null. If the quota is in violation,
041   * there is guaranteed to be a non-null violation policy.
042   */
043  @InterfaceAudience.Private
044  public static class SpaceQuotaStatus implements SpaceQuotaStatusView {
045    private static final SpaceQuotaStatus NOT_IN_VIOLATION = new SpaceQuotaStatus(null, false);
046    final Optional<SpaceViolationPolicy> policy;
047    final boolean inViolation;
048
049    /**
050     * Constructs a {@code SpaceQuotaSnapshot} which is in violation of the provided {@code policy}.
051     * <p/>
052     * Use {@link #notInViolation()} to obtain an instance of this class for the cases when the
053     * quota is not in violation.
054     * @param policy The non-null policy being violated.
055     */
056    public SpaceQuotaStatus(SpaceViolationPolicy policy) {
057      // If the caller is instantiating a status, the policy must be non-null
058      this(Objects.requireNonNull(policy), true);
059    }
060
061    private SpaceQuotaStatus(SpaceViolationPolicy policy, boolean inViolation) {
062      this.policy = Optional.ofNullable(policy);
063      this.inViolation = inViolation;
064    }
065
066    /**
067     * Returns the violation policy, which may be null. It is guaranteed to be non-null if
068     * {@link #isInViolation()} is {@code true}, but may be null otherwise.
069     */
070    @Override
071    public Optional<SpaceViolationPolicy> getPolicy() {
072      return policy;
073    }
074
075    /**
076     * @return {@code true} if the quota is being violated, {@code false} otherwise.
077     */
078    @Override
079    public boolean isInViolation() {
080      return inViolation;
081    }
082
083    /**
084     * Returns a singleton referring to a quota which is not in violation.
085     */
086    public static SpaceQuotaStatus notInViolation() {
087      return NOT_IN_VIOLATION;
088    }
089
090    @Override
091    public int hashCode() {
092      return new HashCodeBuilder().append(policy == null ? 0 : policy.hashCode())
093          .append(inViolation).toHashCode();
094    }
095
096    @Override
097    public boolean equals(Object o) {
098      if (o instanceof SpaceQuotaStatus) {
099        SpaceQuotaStatus other = (SpaceQuotaStatus) o;
100        return Objects.equals(policy, other.policy) && inViolation == other.inViolation;
101      }
102      return false;
103    }
104
105    @Override
106    public String toString() {
107      StringBuilder sb = new StringBuilder(getClass().getSimpleName());
108      sb.append("[policy=").append(policy);
109      sb.append(", inViolation=").append(inViolation).append("]");
110      return sb.toString();
111    }
112
113    public static QuotaProtos.SpaceQuotaStatus toProto(SpaceQuotaStatus status) {
114      QuotaProtos.SpaceQuotaStatus.Builder builder = QuotaProtos.SpaceQuotaStatus.newBuilder();
115      builder.setInViolation(status.inViolation);
116      if (status.isInViolation()) {
117        builder.setViolationPolicy(ProtobufUtil.toProtoViolationPolicy(status.getPolicy().get()));
118      }
119      return builder.build();
120    }
121
122    public static SpaceQuotaStatus toStatus(QuotaProtos.SpaceQuotaStatus proto) {
123      if (proto.getInViolation()) {
124        return new SpaceQuotaStatus(ProtobufUtil.toViolationPolicy(proto.getViolationPolicy()));
125      } else {
126        return NOT_IN_VIOLATION;
127      }
128    }
129  }
130
131  public SpaceQuotaSnapshot(SpaceQuotaStatus quotaStatus, long usage, long limit) {
132    this.quotaStatus = Objects.requireNonNull(quotaStatus);
133    this.usage = usage;
134    this.limit = limit;
135  }
136
137  /**
138   * Returns the status of the quota.
139   */
140  @Override
141  public SpaceQuotaStatus getQuotaStatus() {
142    return quotaStatus;
143  }
144
145  /**
146   * Returns the current usage, in bytes, of the target (e.g. table, namespace).
147   */
148  @Override
149  public long getUsage() {
150    return usage;
151  }
152
153  /**
154   * Returns the limit, in bytes, of the target (e.g. table, namespace).
155   */
156  @Override
157  public long getLimit() {
158    return limit;
159  }
160
161  @Override
162  public int hashCode() {
163    return new HashCodeBuilder()
164        .append(quotaStatus.hashCode())
165        .append(usage)
166        .append(limit)
167        .toHashCode();
168  }
169
170  @Override
171  public boolean equals(Object o) {
172    if (o instanceof SpaceQuotaSnapshot) {
173      SpaceQuotaSnapshot other = (SpaceQuotaSnapshot) o;
174      return quotaStatus.equals(other.quotaStatus) && usage == other.usage && limit == other.limit;
175    }
176    return false;
177  }
178
179  @Override
180  public String toString() {
181    StringBuilder sb = new StringBuilder(32);
182    sb.append("SpaceQuotaSnapshot[policy=").append(quotaStatus).append(", use=");
183    sb.append(StringUtils.byteDesc(usage)).append("/");
184    sb.append(StringUtils.byteDesc(limit)).append("]");
185    return sb.toString();
186  }
187
188  // ProtobufUtil is in hbase-client, and this doesn't need to be public.
189  public static SpaceQuotaSnapshot toSpaceQuotaSnapshot(QuotaProtos.SpaceQuotaSnapshot proto) {
190    return new SpaceQuotaSnapshot(SpaceQuotaStatus.toStatus(proto.getQuotaStatus()),
191        proto.getQuotaUsage(), proto.getQuotaLimit());
192  }
193
194  public static QuotaProtos.SpaceQuotaSnapshot toProtoSnapshot(SpaceQuotaSnapshot snapshot) {
195    return QuotaProtos.SpaceQuotaSnapshot.newBuilder()
196        .setQuotaStatus(SpaceQuotaStatus.toProto(snapshot.getQuotaStatus()))
197        .setQuotaUsage(snapshot.getUsage()).setQuotaLimit(snapshot.getLimit()).build();
198  }
199
200  /**
201   * Returns a singleton that corresponds to no snapshot information.
202   */
203  public static SpaceQuotaSnapshot getNoSuchSnapshot() {
204    return NO_SUCH_SNAPSHOT;
205  }
206}