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.hbase.quotas; 019 020import java.io.Closeable; 021import java.io.IOException; 022import java.util.ArrayDeque; 023import java.util.Iterator; 024import java.util.Objects; 025import java.util.Queue; 026import org.apache.hadoop.conf.Configuration; 027import org.apache.hadoop.hbase.client.Connection; 028import org.apache.hadoop.hbase.client.ConnectionFactory; 029import org.apache.hadoop.hbase.client.Result; 030import org.apache.hadoop.hbase.client.ResultScanner; 031import org.apache.hadoop.hbase.client.Scan; 032import org.apache.hadoop.hbase.client.Table; 033import org.apache.hadoop.hbase.util.Bytes; 034import org.apache.hadoop.util.StringUtils; 035import org.apache.yetus.audience.InterfaceAudience; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039/** 040 * Scanner to iterate over the quota settings. 041 */ 042@InterfaceAudience.Public 043public class QuotaRetriever implements Closeable, Iterable<QuotaSettings> { 044 private static final Logger LOG = LoggerFactory.getLogger(QuotaRetriever.class); 045 046 private final Queue<QuotaSettings> cache = new ArrayDeque<>(); 047 private ResultScanner scanner; 048 /** 049 * Connection to use. Could pass one in and have this class use it but this class wants to be 050 * standalone. 051 */ 052 private Connection connection; 053 private Table table; 054 055 /** 056 * Should QutoaRetriever manage the state of the connection, or leave it be. 057 */ 058 private boolean isManagedConnection = false; 059 060 QuotaRetriever() { 061 } 062 063 void init(final Configuration conf, final Scan scan) throws IOException { 064 // Set this before creating the connection and passing it down to make sure 065 // it's cleaned up if we fail to construct the Scanner. 066 this.isManagedConnection = true; 067 init(ConnectionFactory.createConnection(conf), scan); 068 } 069 070 void init(final Connection conn, final Scan scan) throws IOException { 071 this.connection = Objects.requireNonNull(conn); 072 this.table = this.connection.getTable(QuotaTableUtil.QUOTA_TABLE_NAME); 073 try { 074 scanner = table.getScanner(scan); 075 } catch (IOException e) { 076 try { 077 close(); 078 } catch (IOException ioe) { 079 LOG.warn("Failed getting scanner and then failed close on cleanup", e); 080 } 081 throw e; 082 } 083 } 084 085 @Override 086 public void close() throws IOException { 087 if (this.table != null) { 088 this.table.close(); 089 this.table = null; 090 } 091 // Null out the connection on close() even if we didn't explicitly close it 092 // to maintain typical semantics. 093 if (isManagedConnection) { 094 if (this.connection != null) { 095 this.connection.close(); 096 } 097 } 098 this.connection = null; 099 } 100 101 public QuotaSettings next() throws IOException { 102 if (cache.isEmpty()) { 103 Result result = scanner.next(); 104 // Skip exceedThrottleQuota row key because this is not a QuotaSettings 105 if ( 106 result != null 107 && Bytes.equals(result.getRow(), QuotaTableUtil.getExceedThrottleQuotaRowKey()) 108 ) { 109 result = scanner.next(); 110 } 111 if (result == null) { 112 return null; 113 } 114 QuotaTableUtil.parseResultToCollection(result, cache); 115 } 116 return cache.poll(); 117 } 118 119 @Override 120 public Iterator<QuotaSettings> iterator() { 121 return new Iter(); 122 } 123 124 private class Iter implements Iterator<QuotaSettings> { 125 QuotaSettings cache; 126 127 public Iter() { 128 try { 129 cache = QuotaRetriever.this.next(); 130 } catch (IOException e) { 131 LOG.warn(StringUtils.stringifyException(e)); 132 } 133 } 134 135 @Override 136 public boolean hasNext() { 137 return cache != null; 138 } 139 140 @Override 141 public QuotaSettings next() { 142 QuotaSettings result = cache; 143 try { 144 cache = QuotaRetriever.this.next(); 145 } catch (IOException e) { 146 LOG.warn(StringUtils.stringifyException(e)); 147 } 148 return result; 149 } 150 151 @Override 152 public void remove() { 153 throw new RuntimeException("remove() not supported"); 154 } 155 } 156 157 /** 158 * Open a QuotaRetriever with no filter, all the quota settings will be returned. 159 * @param conf Configuration object to use. 160 * @return the QuotaRetriever 161 * @throws IOException if a remote or network exception occurs 162 */ 163 public static QuotaRetriever open(final Configuration conf) throws IOException { 164 return open(conf, null); 165 } 166 167 /** 168 * Open a QuotaRetriever with the specified filter. 169 * @param conf Configuration object to use. 170 * @param filter the QuotaFilter 171 * @return the QuotaRetriever 172 * @throws IOException if a remote or network exception occurs 173 */ 174 public static QuotaRetriever open(final Configuration conf, final QuotaFilter filter) 175 throws IOException { 176 Scan scan = QuotaTableUtil.makeScan(filter); 177 QuotaRetriever scanner = new QuotaRetriever(); 178 scanner.init(conf, scan); 179 return scanner; 180 } 181}