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