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