001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.fs;
019
020import java.util.Arrays;
021import java.util.List;
022
023import org.apache.hadoop.classification.InterfaceAudience;
024import org.apache.hadoop.classification.InterfaceStability;
025import org.apache.hadoop.util.StringUtils;
026
027/** Store the quota usage of a directory. */
028@InterfaceAudience.Public
029@InterfaceStability.Evolving
030public class QuotaUsage {
031  private long fileAndDirectoryCount;
032  // Make the followings protected so that
033  // deprecated ContentSummary constructor can use them.
034  private long quota;
035  private long spaceConsumed;
036  private long spaceQuota;
037  private long[] typeConsumed;
038  private long[] typeQuota;
039
040  /** Builder class for QuotaUsage. */
041  public static class Builder {
042    public Builder() {
043      this.quota = -1;
044      this.spaceQuota = -1;
045
046      typeConsumed = new long[StorageType.values().length];
047      typeQuota = new long[StorageType.values().length];
048      for (int i = 0; i < typeQuota.length; i++) {
049        typeQuota[i] = -1;
050      }
051    }
052
053    public Builder fileAndDirectoryCount(long count) {
054      this.fileAndDirectoryCount = count;
055      return this;
056    }
057
058    public Builder quota(long quota){
059      this.quota = quota;
060      return this;
061    }
062
063    public Builder spaceConsumed(long spaceConsumed) {
064      this.spaceConsumed = spaceConsumed;
065      return this;
066    }
067
068    public Builder spaceQuota(long spaceQuota) {
069      this.spaceQuota = spaceQuota;
070      return this;
071    }
072
073    public Builder typeConsumed(long[] typeConsumed) {
074      for (int i = 0; i < typeConsumed.length; i++) {
075        this.typeConsumed[i] = typeConsumed[i];
076      }
077      return this;
078    }
079
080    public Builder typeQuota(StorageType type, long quota) {
081      this.typeQuota[type.ordinal()] = quota;
082      return this;
083    }
084
085    public Builder typeConsumed(StorageType type, long consumed) {
086      this.typeConsumed[type.ordinal()] = consumed;
087      return this;
088    }
089
090    public Builder typeQuota(long[] typeQuota) {
091      for (int i = 0; i < typeQuota.length; i++) {
092        this.typeQuota[i] = typeQuota[i];
093      }
094      return this;
095    }
096
097    public QuotaUsage build() {
098      return new QuotaUsage(this);
099    }
100
101    private long fileAndDirectoryCount;
102    private long quota;
103    private long spaceConsumed;
104    private long spaceQuota;
105    private long[] typeConsumed;
106    private long[] typeQuota;
107  }
108
109  // Make it protected for the deprecated ContentSummary constructor.
110  protected QuotaUsage() { }
111
112  /** Build the instance based on the builder. */
113  protected QuotaUsage(Builder builder) {
114    this.fileAndDirectoryCount = builder.fileAndDirectoryCount;
115    this.quota = builder.quota;
116    this.spaceConsumed = builder.spaceConsumed;
117    this.spaceQuota = builder.spaceQuota;
118    this.typeConsumed = builder.typeConsumed;
119    this.typeQuota = builder.typeQuota;
120  }
121
122  protected void setQuota(long quota) {
123    this.quota = quota;
124  }
125
126  protected void setSpaceConsumed(long spaceConsumed) {
127    this.spaceConsumed = spaceConsumed;
128  }
129
130  protected void setSpaceQuota(long spaceQuota) {
131    this.spaceQuota = spaceQuota;
132  }
133
134  /** Return the directory count. */
135  public long getFileAndDirectoryCount() {
136    return fileAndDirectoryCount;
137  }
138
139  /** Return the directory quota. */
140  public long getQuota() {
141    return quota;
142  }
143
144  /** Return (disk) space consumed. */
145  public long getSpaceConsumed() {
146    return spaceConsumed;
147  }
148
149  /** Return (disk) space quota. */
150  public long getSpaceQuota() {
151    return spaceQuota;
152  }
153
154  /** Return storage type quota. */
155  public long getTypeQuota(StorageType type) {
156    return (typeQuota != null) ? typeQuota[type.ordinal()] : -1;
157  }
158
159  /** Return storage type consumed. */
160  public long getTypeConsumed(StorageType type) {
161    return (typeConsumed != null) ? typeConsumed[type.ordinal()] : 0;
162  }
163
164  /** Return storage type quota. */
165  private long[] getTypesQuota() {
166    return typeQuota;
167  }
168
169  /** Return storage type quota. */
170  private long[] getTypesConsumed() {
171    return typeConsumed;
172  }
173
174  /** Return true if any storage type quota has been set. */
175  public boolean isTypeQuotaSet() {
176    if (typeQuota == null) {
177      return false;
178    }
179    for (StorageType t : StorageType.getTypesSupportingQuota()) {
180      if (typeQuota[t.ordinal()] > 0) {
181        return true;
182      }
183    }
184    return false;
185  }
186
187  /** Return true if any storage type consumption information is available. */
188  public boolean isTypeConsumedAvailable() {
189    if (typeConsumed == null) {
190      return false;
191    }
192    for (StorageType t : StorageType.getTypesSupportingQuota()) {
193      if (typeConsumed[t.ordinal()] > 0) {
194        return true;
195      }
196    }
197    return false;
198  }
199
200  @Override
201  public boolean equals(Object to) {
202    return (this == to || (to instanceof QuotaUsage &&
203        getFileAndDirectoryCount() ==
204        ((QuotaUsage) to).getFileAndDirectoryCount() &&
205        getQuota() == ((QuotaUsage) to).getQuota() &&
206        getSpaceConsumed() == ((QuotaUsage) to).getSpaceConsumed() &&
207        getSpaceQuota() == ((QuotaUsage) to).getSpaceQuota() &&
208        Arrays.equals(getTypesQuota(), ((QuotaUsage) to).getTypesQuota()) &&
209        Arrays.equals(getTypesConsumed(),
210        ((QuotaUsage) to).getTypesConsumed())));
211  }
212
213  @Override
214  public int hashCode() {
215    long result = (getFileAndDirectoryCount() ^ getQuota() ^
216        getSpaceConsumed() ^ getSpaceQuota());
217    if (getTypesQuota() != null) {
218      for (long quota : getTypesQuota()) {
219        result ^= quota;
220      }
221    }
222    if (getTypesConsumed() != null) {
223      for (long consumed : getTypesConsumed()) {
224        result ^= consumed;
225      }
226    }
227    return (int)result;
228  }
229
230  /**
231   * Output format:
232   * <----12----> <----15----> <----15----> <----15----> <-------18------->
233   *    QUOTA   REMAINING_QUATA SPACE_QUOTA SPACE_QUOTA_REM FILE_NAME
234   */
235  protected static final String QUOTA_STRING_FORMAT = "%12s %15s ";
236  protected static final String SPACE_QUOTA_STRING_FORMAT = "%15s %15s ";
237
238  protected static final String[] QUOTA_HEADER_FIELDS = new String[] {"QUOTA",
239      "REM_QUOTA", "SPACE_QUOTA", "REM_SPACE_QUOTA"};
240
241  protected static final String QUOTA_HEADER = String.format(
242      QUOTA_STRING_FORMAT + SPACE_QUOTA_STRING_FORMAT,
243      (Object[]) QUOTA_HEADER_FIELDS);
244
245  /**
246   * Output format:
247   * <----12----> <------15-----> <------15-----> <------15----->
248   *        QUOTA       REM_QUOTA     SPACE_QUOTA REM_SPACE_QUOTA
249   * <----12----> <----12----> <-------18------->
250   *    DIR_COUNT   FILE_COUNT       CONTENT_SIZE
251   */
252  private static final String STORAGE_TYPE_SUMMARY_FORMAT = "%13s %17s ";
253
254  /** Return the header of the output.
255   * @return the header of the output
256   */
257  public static String getHeader() {
258    return QUOTA_HEADER;
259  }
260
261  /** default quota display string */
262  private static final String QUOTA_NONE = "none";
263  private static final String QUOTA_INF = "inf";
264
265  @Override
266  public String toString() {
267    return toString(false);
268  }
269
270  public String toString(boolean hOption) {
271    return toString(hOption, false, null);
272  }
273
274  /** Return the string representation of the object in the output format.
275   * if hOption is false file sizes are returned in bytes
276   * if hOption is true file sizes are returned in human readable
277   *
278   * @param hOption a flag indicating if human readable output if to be used
279   * @return the string representation of the object
280   */
281  public String toString(boolean hOption,
282      boolean tOption, List<StorageType> types) {
283    if (tOption) {
284      return getTypesQuotaUsage(hOption, types);
285    }
286    return getQuotaUsage(hOption);
287  }
288
289  protected String getQuotaUsage(boolean hOption) {
290    String quotaStr = QUOTA_NONE;
291    String quotaRem = QUOTA_INF;
292    String spaceQuotaStr = QUOTA_NONE;
293    String spaceQuotaRem = QUOTA_INF;
294
295    if (quota > 0) {
296      quotaStr = formatSize(quota, hOption);
297      quotaRem = formatSize(quota-fileAndDirectoryCount, hOption);
298    }
299    if (spaceQuota >= 0) {
300      spaceQuotaStr = formatSize(spaceQuota, hOption);
301      spaceQuotaRem = formatSize(spaceQuota - spaceConsumed, hOption);
302    }
303
304    return String.format(QUOTA_STRING_FORMAT + SPACE_QUOTA_STRING_FORMAT,
305        quotaStr, quotaRem, spaceQuotaStr, spaceQuotaRem);
306  }
307
308  protected String getTypesQuotaUsage(boolean hOption,
309      List<StorageType> types) {
310    StringBuffer content = new StringBuffer();
311    for (StorageType st : types) {
312      long typeQuota = getTypeQuota(st);
313      long typeConsumed = getTypeConsumed(st);
314      String quotaStr = QUOTA_NONE;
315      String quotaRem = QUOTA_INF;
316
317      if (typeQuota >= 0) {
318        quotaStr = formatSize(typeQuota, hOption);
319        quotaRem = formatSize(typeQuota - typeConsumed, hOption);
320      }
321
322      content.append(String.format(STORAGE_TYPE_SUMMARY_FORMAT,
323          quotaStr, quotaRem));
324    }
325    return content.toString();
326  }
327
328  /**
329   * return the header of with the StorageTypes.
330   *
331   * @param storageTypes
332   * @return storage header string
333   */
334  public static String getStorageTypeHeader(List<StorageType> storageTypes) {
335    StringBuffer header = new StringBuffer();
336
337    for (StorageType st : storageTypes) {
338      /* the field length is 13/17 for quota and remain quota
339       * as the max length for quota name is ARCHIVE_QUOTA
340        * and remain quota name REM_ARCHIVE_QUOTA */
341      String storageName = st.toString();
342      header.append(String.format(STORAGE_TYPE_SUMMARY_FORMAT,
343          storageName + "_QUOTA", "REM_" + storageName + "_QUOTA"));
344    }
345    return header.toString();
346  }
347
348  /**
349   * Formats a size to be human readable or in bytes.
350   * @param size value to be formatted
351   * @param humanReadable flag indicating human readable or not
352   * @return String representation of the size
353  */
354  private String formatSize(long size, boolean humanReadable) {
355    return humanReadable
356      ? StringUtils.TraditionalBinaryPrefix.long2String(size, "", 1)
357      : String.valueOf(size);
358  }
359}