View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.catalog;
19  
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.NavigableMap;
25  import java.util.Set;
26  import java.util.TreeMap;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.hbase.HConstants;
31  import org.apache.hadoop.hbase.HRegionInfo;
32  import org.apache.hadoop.hbase.HTableDescriptor;
33  import org.apache.hadoop.hbase.NotServingRegionException;
34  import org.apache.hadoop.hbase.ServerName;
35  import org.apache.hadoop.hbase.client.Get;
36  import org.apache.hadoop.hbase.client.HTable;
37  import org.apache.hadoop.hbase.client.Result;
38  import org.apache.hadoop.hbase.client.ResultScanner;
39  import org.apache.hadoop.hbase.client.Scan;
40  import org.apache.hadoop.hbase.ipc.HRegionInterface;
41  import org.apache.hadoop.hbase.util.Bytes;
42  import org.apache.hadoop.hbase.util.Pair;
43  import org.apache.hadoop.hbase.util.Writables;
44  import org.apache.hadoop.ipc.RemoteException;
45  
46  /**
47   * Reads region and assignment information from <code>.META.</code>.
48   */
49  public class MetaReader {
50    // TODO: Strip CatalogTracker from this class.  Its all over and in the end
51    // its only used to get its Configuration so we can get associated
52    // Connection.
53    private static final Log LOG = LogFactory.getLog(MetaReader.class);
54  
55    static final byte [] META_REGION_PREFIX;
56    static {
57      // Copy the prefix from FIRST_META_REGIONINFO into META_REGION_PREFIX.
58      // FIRST_META_REGIONINFO == '.META.,,1'.  META_REGION_PREFIX == '.META.,'
59      int len = HRegionInfo.FIRST_META_REGIONINFO.getRegionName().length - 2;
60      META_REGION_PREFIX = new byte [len];
61      System.arraycopy(HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), 0,
62        META_REGION_PREFIX, 0, len);
63    }
64  
65    /**
66     * @param row
67     * @return True if <code>row</code> is row of <code>-ROOT-</code> table.
68     */
69    private static boolean isRootTableRow(final byte [] row) {
70      if (row.length < META_REGION_PREFIX.length + 2 /* ',', + '1' */) {
71        // Can't be meta table region.
72        return false;
73      }
74      // Compare the prefix of row.  If it matches META_REGION_PREFIX prefix,
75      // then this is row from -ROOT_ table.
76      return Bytes.equals(row, 0, META_REGION_PREFIX.length,
77        META_REGION_PREFIX, 0, META_REGION_PREFIX.length);
78    }
79  
80    /**
81     * Performs a full scan of <code>.META.</code>, skipping regions from any
82     * tables in the specified set of disabled tables.
83     * @param catalogTracker
84     * @param disabledTables set of disabled tables that will not be returned
85     * @return Returns a map of every region to it's currently assigned server,
86     * according to META.  If the region does not have an assignment it will have
87     * a null value in the map.
88     * @throws IOException
89     */
90    public static Map<HRegionInfo, ServerName> fullScan(
91        CatalogTracker catalogTracker, final Set<String> disabledTables)
92    throws IOException {
93      return fullScan(catalogTracker, disabledTables, false);
94    }
95  
96    /**
97     * Performs a full scan of <code>.META.</code>, skipping regions from any
98     * tables in the specified set of disabled tables.
99     * @param catalogTracker
100    * @param disabledTables set of disabled tables that will not be returned
101    * @param excludeOfflinedSplitParents If true, do not include offlined split
102    * parents in the return.
103    * @return Returns a map of every region to it's currently assigned server,
104    * according to META.  If the region does not have an assignment it will have
105    * a null value in the map.
106    * @throws IOException
107    */
108   public static Map<HRegionInfo, ServerName> fullScan(
109       CatalogTracker catalogTracker, final Set<String> disabledTables,
110       final boolean excludeOfflinedSplitParents)
111   throws IOException {
112     final Map<HRegionInfo, ServerName> regions =
113       new TreeMap<HRegionInfo, ServerName>();
114     Visitor v = new Visitor() {
115       @Override
116       public boolean visit(Result r) throws IOException {
117         if (r ==  null || r.isEmpty()) return true;
118         Pair<HRegionInfo, ServerName> region = parseCatalogResult(r);
119         if (region == null) return true;
120         HRegionInfo hri = region.getFirst();
121         if (disabledTables.contains(
122             hri.getTableNameAsString())) return true;
123         // Are we to include split parents in the list?
124         if (excludeOfflinedSplitParents && hri.isSplitParent()) return true;
125         regions.put(hri, region.getSecond());
126         return true;
127       }
128     };
129     fullScan(catalogTracker, v);
130     return regions;
131   }
132 
133   /**
134    * Performs a full scan of <code>.META.</code>.
135    * @return List of {@link Result}
136    * @throws IOException
137    */
138   public static List<Result> fullScan(CatalogTracker catalogTracker)
139   throws IOException {
140     CollectAllVisitor v = new CollectAllVisitor();
141     fullScan(catalogTracker, v, null);
142     return v.getResults();
143   }
144 
145   /**
146    * Performs a full scan of a <code>-ROOT-</code> table.
147    * @return List of {@link Result}
148    * @throws IOException
149    */
150   public static List<Result> fullScanOfRoot(CatalogTracker catalogTracker)
151   throws IOException {
152     CollectAllVisitor v = new CollectAllVisitor();
153     fullScan(catalogTracker, v, null, true);
154     return v.getResults();
155   }
156 
157   /**
158    * Performs a full scan of <code>.META.</code>.
159    * @param catalogTracker
160    * @param visitor Visitor invoked against each row.
161    * @throws IOException
162    */
163   public static void fullScan(CatalogTracker catalogTracker,
164       final Visitor visitor)
165   throws IOException {
166     fullScan(catalogTracker, visitor, null);
167   }
168 
169   /**
170    * Performs a full scan of <code>.META.</code>.
171    * @param catalogTracker
172    * @param visitor Visitor invoked against each row.
173    * @param startrow Where to start the scan. Pass null if want to begin scan
174    * at first row (The visitor will stop the Scan when its done so no need to
175    * pass a stoprow).
176    * @throws IOException
177    */
178   public static void fullScan(CatalogTracker catalogTracker,
179       final Visitor visitor, final byte [] startrow)
180   throws IOException {
181     fullScan(catalogTracker, visitor, startrow, false);
182   }
183 
184   /**
185    * Callers should call close on the returned {@link HTable} instance.
186    * @param catalogTracker We'll use this catalogtracker's connection
187    * @param tableName Table to get an {@link HTable} against.
188    * @return An {@link HTable} for <code>tableName</code>
189    * @throws IOException
190    */
191   private static HTable getHTable(final CatalogTracker catalogTracker,
192       final byte [] tableName)
193   throws IOException {
194     // Passing the CatalogTracker's connection configuration ensures this
195     // HTable instance uses the CatalogTracker's connection.
196     org.apache.hadoop.hbase.client.HConnection c = catalogTracker.getConnection();
197     if (c == null) throw new NullPointerException("No connection");
198     return new HTable(catalogTracker.getConnection().getConfiguration(), tableName);
199   }
200 
201   /**
202    * Callers should call close on the returned {@link HTable} instance.
203    * @param catalogTracker
204    * @param row Row we are putting 
205    * @return
206    * @throws IOException
207    */
208   static HTable getCatalogHTable(final CatalogTracker catalogTracker,
209       final byte [] row)
210   throws IOException {
211     return isRootTableRow(row)?
212       getRootHTable(catalogTracker):
213       getMetaHTable(catalogTracker);
214   }
215 
216   /**
217    * Callers should call close on the returned {@link HTable} instance.
218    * @param ct
219    * @return An {@link HTable} for <code>.META.</code>
220    * @throws IOException
221    */
222   static HTable getMetaHTable(final CatalogTracker ct)
223   throws IOException {
224     return getHTable(ct, HConstants.META_TABLE_NAME);
225   }
226 
227   /**
228    * Callers should call close on the returned {@link HTable} instance.
229    * @param ct
230    * @return An {@link HTable} for <code>-ROOT-</code>
231    * @throws IOException
232    */
233   static HTable getRootHTable(final CatalogTracker ct)
234   throws IOException {
235     return getHTable(ct, HConstants.ROOT_TABLE_NAME);
236   }
237 
238   /**
239    * @param t Table to use (will be closed when done).
240    * @param g Get to run
241    * @throws IOException
242    */
243   private static Result get(final HTable t, final Get g) throws IOException {
244     try {
245       return t.get(g);
246     } finally {
247       t.close();
248     }
249   }
250 
251   /**
252    * Reads the location of META from ROOT.
253    * @param metaServer connection to server hosting ROOT
254    * @return location of META in ROOT where location, or null if not available
255    * @throws IOException
256    * @deprecated Does not retry; use #getMetaRegionLocation(CatalogTracker)
257    */
258   public static ServerName readMetaLocation(HRegionInterface metaServer)
259   throws IOException {
260     return readLocation(metaServer, CatalogTracker.ROOT_REGION_NAME,
261         CatalogTracker.META_REGION_NAME);
262   }
263 
264   /**
265    * Gets the location of <code>.META.</code> region by reading content of
266    * <code>-ROOT-</code>.
267    * @param ct
268    * @return location of <code>.META.</code> region as a {@link ServerName} or
269    * null if not found
270    * @throws IOException
271    */
272   static ServerName getMetaRegionLocation(final CatalogTracker ct)
273   throws IOException {
274     return MetaReader.readRegionLocation(ct, CatalogTracker.META_REGION_NAME);
275   }
276 
277   /**
278    * Reads the location of the specified region
279    * @param catalogTracker
280    * @param regionName region whose location we are after
281    * @return location of region as a {@link ServerName} or null if not found
282    * @throws IOException
283    */
284   static ServerName readRegionLocation(CatalogTracker catalogTracker,
285       byte [] regionName)
286   throws IOException {
287     Pair<HRegionInfo, ServerName> pair = getRegion(catalogTracker, regionName);
288     return (pair == null || pair.getSecond() == null)? null: pair.getSecond();
289   }
290 
291   // TODO: Remove when deprecated dependencies are removed.
292   private static ServerName readLocation(HRegionInterface metaServer,
293       byte [] catalogRegionName, byte [] regionName)
294   throws IOException {
295     Result r = null;
296     try {
297       r = metaServer.get(catalogRegionName,
298         new Get(regionName).
299         addColumn(HConstants.CATALOG_FAMILY,
300           HConstants.SERVER_QUALIFIER).
301         addColumn(HConstants.CATALOG_FAMILY,
302           HConstants.STARTCODE_QUALIFIER));
303     } catch (java.net.SocketTimeoutException e) {
304       // Treat this exception + message as unavailable catalog table. Catch it
305       // and fall through to return a null
306     } catch (java.net.SocketException e) {
307       // Treat this exception + message as unavailable catalog table. Catch it
308       // and fall through to return a null
309     } catch (RemoteException re) {
310       IOException ioe = re.unwrapRemoteException();
311       if (ioe instanceof NotServingRegionException) {
312         // Treat this NSRE as unavailable table.  Catch and fall through to
313         // return null below
314       } else if (ioe.getMessage().contains("Server not running")) {
315         // Treat as unavailable table.
316       } else {
317         throw re;
318       }
319     } catch (IOException e) {
320       if (e.getCause() != null && e.getCause() instanceof IOException &&
321           e.getCause().getMessage() != null &&
322           e.getCause().getMessage().contains("Connection reset by peer")) {
323         // Treat this exception + message as unavailable catalog table. Catch it
324         // and fall through to return a null
325       } else {
326         throw e;
327       }
328     }
329     if (r == null || r.isEmpty()) {
330       return null;
331     }
332     return getServerNameFromCatalogResult(r);
333   }
334 
335   /**
336    * Gets the region info and assignment for the specified region.
337    * @param catalogTracker
338    * @param regionName Region to lookup.
339    * @return Location and HRegionInfo for <code>regionName</code>
340    * @throws IOException
341    */
342   public static Pair<HRegionInfo, ServerName> getRegion(
343       CatalogTracker catalogTracker, byte [] regionName)
344   throws IOException {
345     Get get = new Get(regionName);
346     get.addFamily(HConstants.CATALOG_FAMILY);
347     Result r = get(getCatalogHTable(catalogTracker, regionName), get);
348     return (r == null || r.isEmpty())? null: parseCatalogResult(r);
349   }
350 
351   /**
352    * Extract a {@link ServerName}
353    * For use on catalog table {@link Result}.
354    * @param r Result to pull from
355    * @return A ServerName instance or null if necessary fields not found or empty.
356    */
357   public static ServerName getServerNameFromCatalogResult(final Result r) {
358     byte[] value = r.getValue(HConstants.CATALOG_FAMILY,
359       HConstants.SERVER_QUALIFIER);
360     if (value == null || value.length == 0) return null;
361     String hostAndPort = Bytes.toString(value);
362     value = r.getValue(HConstants.CATALOG_FAMILY,
363       HConstants.STARTCODE_QUALIFIER);
364     if (value == null || value.length == 0) return null;
365     return new ServerName(hostAndPort, Bytes.toLong(value));
366   }
367 
368   /**
369    * Extract a HRegionInfo and ServerName.
370    * For use on catalog table {@link Result}.
371    * @param r Result to pull from
372    * @return A pair of the {@link HRegionInfo} and the {@link ServerName}
373    * (or null for server address if no address set in .META.).
374    * @throws IOException
375    */
376   public static Pair<HRegionInfo, ServerName> parseCatalogResult(final Result r)
377   throws IOException {
378     HRegionInfo info =
379       parseHRegionInfoFromCatalogResult(r, HConstants.REGIONINFO_QUALIFIER);
380     ServerName sn = getServerNameFromCatalogResult(r);
381     return new Pair<HRegionInfo, ServerName>(info, sn);
382   }
383 
384   /**
385    * Parse the content of the cell at {@link HConstants#CATALOG_FAMILY} and
386    * <code>qualifier</code> as an HRegionInfo and return it, or null.
387    * For use on catalog table {@link Result}.
388    * @param r Result instance to pull from.
389    * @param qualifier Column family qualifier -- either
390    * {@link HConstants#SPLITA_QUALIFIER}, {@link HConstants#SPLITB_QUALIFIER} or
391    * {@link HConstants#REGIONINFO_QUALIFIER}.
392    * @return An HRegionInfo instance or null.
393    * @throws IOException
394    */
395   public static HRegionInfo parseHRegionInfoFromCatalogResult(final Result r,
396       byte [] qualifier)
397   throws IOException {
398     byte [] bytes = r.getValue(HConstants.CATALOG_FAMILY, qualifier);
399     if (bytes == null || bytes.length <= 0) return null;
400     return Writables.getHRegionInfoOrNull(bytes);
401   }
402 
403   /**
404    * Checks if the specified table exists.  Looks at the META table hosted on
405    * the specified server.
406    * @param catalogTracker
407    * @param tableName table to check
408    * @return true if the table exists in meta, false if not
409    * @throws IOException
410    */
411   public static boolean tableExists(CatalogTracker catalogTracker,
412       String tableName)
413   throws IOException {
414     if (tableName.equals(HTableDescriptor.ROOT_TABLEDESC.getNameAsString()) ||
415         tableName.equals(HTableDescriptor.META_TABLEDESC.getNameAsString())) {
416       // Catalog tables always exist.
417       return true;
418     }
419     final byte [] tableNameBytes = Bytes.toBytes(tableName);
420     // Make a version of ResultCollectingVisitor that only collects the first
421     CollectingVisitor<HRegionInfo> visitor = new CollectingVisitor<HRegionInfo>() {
422       private HRegionInfo current = null;
423 
424       @Override
425       public boolean visit(Result r) throws IOException {
426         this.current =
427           parseHRegionInfoFromCatalogResult(r, HConstants.REGIONINFO_QUALIFIER);
428         if (this.current == null) {
429           LOG.warn("No serialized HRegionInfo in " + r);
430           return true;
431         }
432         if (!isInsideTable(this.current, tableNameBytes)) return false;
433         if (this.current.isSplitParent()) return true;
434         // Else call super and add this Result to the collection.
435         super.visit(r);
436         // Stop collecting regions from table after we get one.
437         return false;
438       }
439 
440       @Override
441       void add(Result r) {
442         // Add the current HRI.
443         this.results.add(this.current);
444       }
445     };
446     fullScan(catalogTracker, visitor, getTableStartRowForMeta(tableNameBytes));
447     // If visitor has results >= 1 then table exists.
448     return visitor.getResults().size() >= 1;
449   }
450 
451   /**
452    * Gets all of the regions of the specified table.
453    * @param catalogTracker
454    * @param tableName
455    * @return Ordered list of {@link HRegionInfo}.
456    * @throws IOException
457    */
458   public static List<HRegionInfo> getTableRegions(CatalogTracker catalogTracker,
459       byte [] tableName)
460   throws IOException {
461     return getTableRegions(catalogTracker, tableName, false);
462   }
463 
464   /**
465    * Gets all of the regions of the specified table.
466    * @param catalogTracker
467    * @param tableName
468    * @param excludeOfflinedSplitParents If true, do not include offlined split
469    * parents in the return.
470    * @return Ordered list of {@link HRegionInfo}.
471    * @throws IOException
472    */
473   public static List<HRegionInfo> getTableRegions(CatalogTracker catalogTracker,
474       byte [] tableName, final boolean excludeOfflinedSplitParents)
475   throws IOException {
476     List<Pair<HRegionInfo, ServerName>> result = null;
477     try {
478       result = getTableRegionsAndLocations(catalogTracker, tableName,
479         excludeOfflinedSplitParents);
480     } catch (InterruptedException e) {
481       throw new RuntimeException(e);
482     }
483     return getListOfHRegionInfos(result);
484   }
485 
486   static List<HRegionInfo> getListOfHRegionInfos(final List<Pair<HRegionInfo, ServerName>> pairs) {
487     if (pairs == null || pairs.isEmpty()) return null;
488     List<HRegionInfo> result = new ArrayList<HRegionInfo>(pairs.size());
489     for (Pair<HRegionInfo, ServerName> pair: pairs) {
490       result.add(pair.getFirst());
491     }
492     return result;
493   }
494 
495   /**
496    * @param current
497    * @param tableName
498    * @return True if <code>current</code> tablename is equal to
499    * <code>tableName</code>
500    */
501   static boolean isInsideTable(final HRegionInfo current, final byte [] tableName) {
502     return Bytes.equals(tableName, current.getTableName());
503   }
504 
505   /**
506    * @param tableName
507    * @return Place to start Scan in <code>.META.</code> when passed a
508    * <code>tableName</code>; returns &lt;tableName&rt; &lt;,&rt; &lt;,&rt;
509    */
510   static byte [] getTableStartRowForMeta(final byte [] tableName) {
511     byte [] startRow = new byte[tableName.length + 2];
512     System.arraycopy(tableName, 0, startRow, 0, tableName.length);
513     startRow[startRow.length - 2] = HRegionInfo.DELIMITER;
514     startRow[startRow.length - 1] = HRegionInfo.DELIMITER;
515     return startRow;
516   }
517 
518   /**
519    * This method creates a Scan object that will only scan catalog rows that
520    * belong to the specified table. It doesn't specify any columns.
521    * This is a better alternative to just using a start row and scan until
522    * it hits a new table since that requires parsing the HRI to get the table
523    * name.
524    * @param tableName bytes of table's name
525    * @return configured Scan object
526    */
527   public static Scan getScanForTableName(byte[] tableName) {
528     String strName = Bytes.toString(tableName);
529     // Start key is just the table name with delimiters
530     byte[] startKey = Bytes.toBytes(strName + ",,");
531     // Stop key appends the smallest possible char to the table name
532     byte[] stopKey = Bytes.toBytes(strName + " ,,");
533 
534     Scan scan = new Scan(startKey);
535     scan.setStopRow(stopKey);
536     return scan;
537   }
538 
539   /**
540    * @param catalogTracker
541    * @param tableName
542    * @return Return list of regioninfos and server.
543    * @throws IOException
544    * @throws InterruptedException
545    */
546   public static List<Pair<HRegionInfo, ServerName>>
547   getTableRegionsAndLocations(CatalogTracker catalogTracker, String tableName)
548   throws IOException, InterruptedException {
549     return getTableRegionsAndLocations(catalogTracker, Bytes.toBytes(tableName),
550       true);
551   }
552 
553   /**
554    * @param catalogTracker
555    * @param tableName
556    * @return Return list of regioninfos and server addresses.
557    * @throws IOException
558    * @throws InterruptedException
559    */
560   public static List<Pair<HRegionInfo, ServerName>>
561   getTableRegionsAndLocations(final CatalogTracker catalogTracker,
562       final byte [] tableName, final boolean excludeOfflinedSplitParents)
563   throws IOException, InterruptedException {
564     if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
565       // If root, do a bit of special handling.
566       ServerName serverName = catalogTracker.getRootLocation();
567       List<Pair<HRegionInfo, ServerName>> list =
568         new ArrayList<Pair<HRegionInfo, ServerName>>();
569       list.add(new Pair<HRegionInfo, ServerName>(HRegionInfo.ROOT_REGIONINFO,
570         serverName));
571       return list;
572     }
573     // Make a version of CollectingVisitor that collects HRegionInfo and ServerAddress
574     CollectingVisitor<Pair<HRegionInfo, ServerName>> visitor =
575         new CollectingVisitor<Pair<HRegionInfo, ServerName>>() {
576       private Pair<HRegionInfo, ServerName> current = null;
577 
578       @Override
579       public boolean visit(Result r) throws IOException {
580         HRegionInfo hri =
581           parseHRegionInfoFromCatalogResult(r, HConstants.REGIONINFO_QUALIFIER);
582         if (hri == null) {
583           LOG.warn("No serialized HRegionInfo in " + r);
584           return true;
585         }
586         if (!isInsideTable(hri, tableName)) return false;
587         if (excludeOfflinedSplitParents && hri.isSplitParent()) return true;
588         ServerName sn = getServerNameFromCatalogResult(r);
589         // Populate this.current so available when we call #add
590         this.current = new Pair<HRegionInfo, ServerName>(hri, sn);
591         // Else call super and add this Result to the collection.
592         return super.visit(r);
593       }
594 
595       @Override
596       void add(Result r) {
597         this.results.add(this.current);
598       }
599     };
600     fullScan(catalogTracker, visitor, getTableStartRowForMeta(tableName),
601       Bytes.equals(tableName, HConstants.META_TABLE_NAME));
602     return visitor.getResults();
603   }
604 
605   /**
606    * @param catalogTracker
607    * @param serverName
608    * @return List of user regions installed on this server (does not include
609    * catalog regions).
610    * @throws IOException
611    */
612   public static NavigableMap<HRegionInfo, Result>
613   getServerUserRegions(CatalogTracker catalogTracker, final ServerName serverName)
614   throws IOException {
615     final NavigableMap<HRegionInfo, Result> hris = new TreeMap<HRegionInfo, Result>();
616     // Fill the above hris map with entries from .META. that have the passed
617     // servername.
618     CollectingVisitor<Result> v = new CollectingVisitor<Result>() {
619       @Override
620       void add(Result r) {
621         if (r == null || r.isEmpty()) return;
622         ServerName sn = getServerNameFromCatalogResult(r);
623         if (sn != null && sn.equals(serverName)) this.results.add(r); 
624       }
625     };
626     fullScan(catalogTracker, v);
627     List<Result> results = v.getResults();
628     if (results != null && !results.isEmpty()) {
629       // Convert results to Map keyed by HRI
630       for (Result r: results) {
631         Pair<HRegionInfo, ServerName> p = parseCatalogResult(r);
632         if (p != null && p.getFirst() != null) hris.put(p.getFirst(), r);
633       }
634     }
635     return hris;
636   }
637 
638   public static void fullScanMetaAndPrint(final CatalogTracker catalogTracker)
639   throws IOException {
640     Visitor v = new Visitor() {
641       @Override
642       public boolean visit(Result r) throws IOException {
643         if (r ==  null || r.isEmpty()) return true;
644         LOG.info("fullScanMetaAndPrint.Current Meta Row: " + r);
645         HRegionInfo hrim = MetaEditor.getHRegionInfo(r);
646         LOG.info("fullScanMetaAndPrint.HRI Print= " + hrim);
647         return true;
648       }
649     };
650     fullScan(catalogTracker, v);
651   }
652 
653   /**
654    * Fully scan a given region, on a given server starting with given row.
655    * @param hRegionInterface region server
656    * @param visitor visitor
657    * @param regionName name of region
658    * @param startrow start row
659    * @throws IOException
660    * @deprecated Does not retry; use fullScan xxx instead.
661    x
662    */
663   public static void fullScan(HRegionInterface hRegionInterface,
664                               Visitor visitor, final byte[] regionName,
665                               byte[] startrow) throws IOException {
666     if (hRegionInterface == null) return;
667     Scan scan = new Scan();
668     if (startrow != null) scan.setStartRow(startrow);
669     scan.addFamily(HConstants.CATALOG_FAMILY);
670     long scannerid = hRegionInterface.openScanner(regionName, scan);
671     try {
672       Result data;
673       while((data = hRegionInterface.next(scannerid)) != null) {
674         if (!data.isEmpty()) visitor.visit(data);
675       }
676     } finally {
677       hRegionInterface.close(scannerid);
678     }
679     return;
680   }
681 
682   /**
683    * Performs a full scan of a catalog table.
684    * @param catalogTracker
685    * @param visitor Visitor invoked against each row.
686    * @param startrow Where to start the scan. Pass null if want to begin scan
687    * at first row.
688    * @param scanRoot True if we are to scan <code>-ROOT-</code> rather than
689    * <code>.META.</code>, the default (pass false to scan .META.)
690    * @throws IOException
691    */
692   static void fullScan(CatalogTracker catalogTracker,
693     final Visitor visitor, final byte [] startrow, final boolean scanRoot)
694   throws IOException {
695     Scan scan = new Scan();
696     if (startrow != null) scan.setStartRow(startrow);
697     scan.addFamily(HConstants.CATALOG_FAMILY);
698     HTable metaTable = scanRoot?
699       getRootHTable(catalogTracker): getMetaHTable(catalogTracker);
700     ResultScanner scanner = metaTable.getScanner(scan);
701     try {
702       Result data;
703       while((data = scanner.next()) != null) {
704         if (data.isEmpty()) continue;
705         // Break if visit returns false.
706         if (!visitor.visit(data)) break;
707       }
708     } finally {
709       scanner.close();
710       metaTable.close();
711     }
712     return;
713   }
714 
715   /**
716    * Implementations 'visit' a catalog table row.
717    */
718   public interface Visitor {
719     /**
720      * Visit the catalog table row.
721      * @param r A row from catalog table
722      * @return True if we are to proceed scanning the table, else false if
723      * we are to stop now.
724      */
725     public boolean visit(final Result r) throws IOException;
726   }
727 
728   /**
729    * A {@link Visitor} that collects content out of passed {@link Result}.
730    */
731   static abstract class CollectingVisitor<T> implements Visitor {
732     final List<T> results = new ArrayList<T>();
733     @Override
734     public boolean visit(Result r) throws IOException {
735       if (r ==  null || r.isEmpty()) return true;
736       add(r);
737       return true;
738     }
739 
740     abstract void add(Result r);
741 
742     /**
743      * @return Collected results; wait till visits complete to collect all
744      * possible results
745      */
746     List<T> getResults() {
747       return this.results;
748     }
749   }
750 
751   /**
752    * Collects all returned.
753    */
754   static class CollectAllVisitor extends CollectingVisitor<Result> {
755     @Override
756     void add(Result r) {
757       this.results.add(r);
758     }
759   }
760 }