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