View Javadoc

1   /**
2    * The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.migration;
21  
22  import java.io.IOException;
23  import java.util.Arrays;
24  import java.util.Comparator;
25  import java.util.List;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.fs.FSDataInputStream;
31  import org.apache.hadoop.fs.FileStatus;
32  import org.apache.hadoop.fs.FileSystem;
33  import org.apache.hadoop.fs.Path;
34  import org.apache.hadoop.fs.PathFilter;
35  import org.apache.hadoop.hbase.Cell;
36  import org.apache.hadoop.hbase.CellUtil;
37  import org.apache.hadoop.hbase.HConstants;
38  import org.apache.hadoop.hbase.HRegionInfo;
39  import org.apache.hadoop.hbase.HTableDescriptor;
40  import org.apache.hadoop.hbase.NamespaceDescriptor;
41  import org.apache.hadoop.hbase.ServerName;
42  import org.apache.hadoop.hbase.TableName;
43  import org.apache.hadoop.hbase.MetaTableAccessor;
44  import org.apache.hadoop.hbase.client.Delete;
45  import org.apache.hadoop.hbase.client.Get;
46  import org.apache.hadoop.hbase.client.Put;
47  import org.apache.hadoop.hbase.client.Result;
48  import org.apache.hadoop.hbase.exceptions.DeserializationException;
49  import org.apache.hadoop.hbase.regionserver.HRegion;
50  import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
51  import org.apache.hadoop.hbase.wal.WAL;
52  import org.apache.hadoop.hbase.wal.WALFactory;
53  import org.apache.hadoop.hbase.security.access.AccessControlLists;
54  import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
55  import org.apache.hadoop.hbase.util.Bytes;
56  import org.apache.hadoop.hbase.util.FSTableDescriptors;
57  import org.apache.hadoop.hbase.util.FSUtils;
58  import org.apache.hadoop.util.Tool;
59  
60  import com.google.common.collect.Lists;
61  import com.google.common.primitives.Ints;
62  
63  /**
64   * Upgrades old 0.94 filesystem layout to namespace layout
65   * Does the following:
66   *
67   * - creates system namespace directory and move .META. table there
68   * renaming .META. table to hbase:meta,
69   * this in turn would require to re-encode the region directory name
70   *
71   * <p>The pre-0.96 paths and dir names are hardcoded in here.
72   */
73  public class NamespaceUpgrade implements Tool {
74    private static final Log LOG = LogFactory.getLog(NamespaceUpgrade.class);
75  
76    private Configuration conf;
77  
78    private FileSystem fs;
79  
80    private Path rootDir;
81    private Path sysNsDir;
82    private Path defNsDir;
83    private Path baseDirs[];
84    private Path backupDir;
85    // First move everything to this tmp .data dir in case there is a table named 'data'
86    private static final String TMP_DATA_DIR = ".data";
87    // Old dir names to migrate.
88    private static final String DOT_LOGS = ".logs";
89    private static final String DOT_OLD_LOGS = ".oldlogs";
90    private static final String DOT_CORRUPT = ".corrupt";
91    private static final String DOT_SPLITLOG = "splitlog";
92    private static final String DOT_ARCHIVE = ".archive";
93  
94    // The old default directory of hbase.dynamic.jars.dir(0.94.12 release).
95    private static final String DOT_LIB_DIR = ".lib";
96  
97    private static final String OLD_ACL = "_acl_";
98    /** Directories that are not HBase table directories */
99    static final List<String> NON_USER_TABLE_DIRS = Arrays.asList(new String[] {
100       DOT_LOGS,
101       DOT_OLD_LOGS,
102       DOT_CORRUPT,
103       DOT_SPLITLOG,
104       HConstants.HBCK_SIDELINEDIR_NAME,
105       DOT_ARCHIVE,
106       HConstants.SNAPSHOT_DIR_NAME,
107       HConstants.HBASE_TEMP_DIRECTORY,
108       TMP_DATA_DIR,
109       OLD_ACL,
110       DOT_LIB_DIR});
111 
112   public NamespaceUpgrade() throws IOException {
113     super();
114   }
115 
116   public void init() throws IOException {
117     this.rootDir = FSUtils.getRootDir(conf);
118     FSUtils.setFsDefault(getConf(), rootDir);
119     this.fs = FileSystem.get(conf);
120     Path tmpDataDir = new Path(rootDir, TMP_DATA_DIR);
121     sysNsDir = new Path(tmpDataDir, NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR);
122     defNsDir = new Path(tmpDataDir, NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR);
123     baseDirs = new Path[]{rootDir,
124         new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY),
125         new Path(rootDir, HConstants.HBASE_TEMP_DIRECTORY)};
126     backupDir = new Path(rootDir, HConstants.MIGRATION_NAME);
127   }
128 
129 
130   public void upgradeTableDirs() throws IOException, DeserializationException {
131     // if new version is written then upgrade is done
132     if (verifyNSUpgrade(fs, rootDir)) {
133       return;
134     }
135 
136     makeNamespaceDirs();
137 
138     migrateTables();
139 
140     migrateSnapshots();
141 
142     migrateDotDirs();
143 
144     migrateMeta();
145 
146     migrateACL();
147 
148     deleteRoot();
149 
150     FSUtils.setVersion(fs, rootDir);
151   }
152 
153   /**
154    * Remove the -ROOT- dir. No longer of use.
155    * @throws IOException
156    */
157   public void deleteRoot() throws IOException {
158     Path rootDir = new Path(this.rootDir, "-ROOT-");
159     if (this.fs.exists(rootDir)) {
160       if (!this.fs.delete(rootDir, true)) LOG.info("Failed remove of " + rootDir);
161       LOG.info("Deleted " + rootDir);
162     }
163   }
164 
165   /**
166    * Rename all the dot dirs -- .data, .archive, etc. -- as data, archive, etc.; i.e. minus the dot.
167    * @throws IOException
168    */
169   public void migrateDotDirs() throws IOException {
170     // Dot dirs to rename.  Leave the tmp dir named '.tmp' and snapshots as .hbase-snapshot.
171     final Path archiveDir = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
172     Path [][] dirs = new Path[][] {
173       new Path [] {new Path(rootDir, DOT_CORRUPT), new Path(rootDir, HConstants.CORRUPT_DIR_NAME)},
174       new Path [] {new Path(rootDir, DOT_LOGS), new Path(rootDir, HConstants.HREGION_LOGDIR_NAME)},
175       new Path [] {new Path(rootDir, DOT_OLD_LOGS),
176         new Path(rootDir, HConstants.HREGION_OLDLOGDIR_NAME)},
177       new Path [] {new Path(rootDir, TMP_DATA_DIR),
178         new Path(rootDir, HConstants.BASE_NAMESPACE_DIR)},
179       new Path[] { new Path(rootDir, DOT_LIB_DIR),
180         new Path(rootDir, HConstants.LIB_DIR)}};
181     for (Path [] dir: dirs) {
182       Path src = dir[0];
183       Path tgt = dir[1];
184       if (!this.fs.exists(src)) {
185         LOG.info("Does not exist: " + src);
186         continue;
187       }
188       rename(src, tgt);
189     }
190     // Do the .archive dir.  Need to move its subdirs to the default ns dir under data dir... so
191     // from '.archive/foo', to 'archive/data/default/foo'.
192     Path oldArchiveDir = new Path(rootDir, DOT_ARCHIVE);
193     if (this.fs.exists(oldArchiveDir)) {
194       // This is a pain doing two nn calls but portable over h1 and h2.
195       mkdirs(archiveDir);
196       Path archiveDataDir = new Path(archiveDir, HConstants.BASE_NAMESPACE_DIR);
197       mkdirs(archiveDataDir);
198       rename(oldArchiveDir, new Path(archiveDataDir,
199         NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR));
200     }
201     // Update the system and user namespace dirs removing the dot in front of .data.
202     Path dataDir = new Path(rootDir, HConstants.BASE_NAMESPACE_DIR);
203     sysNsDir = new Path(dataDir, NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR);
204     defNsDir = new Path(dataDir, NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR);
205   }
206 
207   private void mkdirs(final Path p) throws IOException {
208     if (!this.fs.mkdirs(p)) throw new IOException("Failed make of " + p);
209   }
210 
211   private void rename(final Path src, final Path tgt) throws IOException {
212     if (!fs.rename(src, tgt)) {
213       throw new IOException("Failed move " + src + " to " + tgt);
214     }
215   }
216 
217   /**
218    * Create the system and default namespaces dirs
219    * @throws IOException
220    */
221   public void makeNamespaceDirs() throws IOException {
222     if (!fs.exists(sysNsDir)) {
223       if (!fs.mkdirs(sysNsDir)) {
224         throw new IOException("Failed to create system namespace dir: " + sysNsDir);
225       }
226     }
227     if (!fs.exists(defNsDir)) {
228       if (!fs.mkdirs(defNsDir)) {
229         throw new IOException("Failed to create default namespace dir: " + defNsDir);
230       }
231     }
232   }
233 
234   /**
235    * Migrate all tables into respective namespaces, either default or system.  We put them into
236    * a temporary location, '.data', in case a user table is name 'data'.  In a later method we will
237    * move stuff from .data to data.
238    * @throws IOException
239    */
240   public void migrateTables() throws IOException {
241     List<String> sysTables = Lists.newArrayList("-ROOT-",".META.", ".META");
242 
243     // Migrate tables including archive and tmp
244     for (Path baseDir: baseDirs) {
245       if (!fs.exists(baseDir)) continue;
246       List<Path> oldTableDirs = FSUtils.getLocalTableDirs(fs, baseDir);
247       for (Path oldTableDir: oldTableDirs) {
248         if (NON_USER_TABLE_DIRS.contains(oldTableDir.getName())) continue;
249         if (sysTables.contains(oldTableDir.getName())) continue;
250         // Make the new directory under the ns to which we will move the table.
251         Path nsDir = new Path(this.defNsDir,
252           TableName.valueOf(oldTableDir.getName()).getQualifierAsString());
253         LOG.info("Moving " + oldTableDir + " to " + nsDir);
254         if (!fs.exists(nsDir.getParent())) {
255           if (!fs.mkdirs(nsDir.getParent())) {
256             throw new IOException("Failed to create namespace dir "+nsDir.getParent());
257           }
258         }
259         if (sysTables.indexOf(oldTableDir.getName()) < 0) {
260           LOG.info("Migrating table " + oldTableDir.getName() + " to " + nsDir);
261           if (!fs.rename(oldTableDir, nsDir)) {
262             throw new IOException("Failed to move "+oldTableDir+" to namespace dir "+nsDir);
263           }
264         }
265       }
266     }
267   }
268 
269   public void migrateSnapshots() throws IOException {
270     //migrate snapshot dir
271     Path oldSnapshotDir = new Path(rootDir, HConstants.OLD_SNAPSHOT_DIR_NAME);
272     Path newSnapshotDir = new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
273     if (fs.exists(oldSnapshotDir)) {
274       boolean foundOldSnapshotDir = false;
275       // Logic to verify old snapshot dir culled from SnapshotManager
276       // ignore all the snapshots in progress
277       FileStatus[] snapshots = fs.listStatus(oldSnapshotDir,
278         new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs));
279       // loop through all the completed snapshots
280       for (FileStatus snapshot : snapshots) {
281         Path info = new Path(snapshot.getPath(), SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
282         // if the snapshot is bad
283         if (fs.exists(info)) {
284           foundOldSnapshotDir = true;
285           break;
286         }
287       }
288       if(foundOldSnapshotDir) {
289         LOG.info("Migrating snapshot dir");
290         if (!fs.rename(oldSnapshotDir, newSnapshotDir)) {
291           throw new IOException("Failed to move old snapshot dir "+
292               oldSnapshotDir+" to new "+newSnapshotDir);
293         }
294       }
295     }
296   }
297 
298   public void migrateMeta() throws IOException {
299     Path newMetaDir = new Path(this.sysNsDir, TableName.META_TABLE_NAME.getQualifierAsString());
300     Path newMetaRegionDir =
301       new Path(newMetaDir, HRegionInfo.FIRST_META_REGIONINFO.getEncodedName());
302     Path oldMetaDir = new Path(rootDir, ".META.");
303     if (fs.exists(oldMetaDir)) {
304       LOG.info("Migrating meta table " + oldMetaDir.getName() + " to " + newMetaDir);
305       if (!fs.rename(oldMetaDir, newMetaDir)) {
306         throw new IOException("Failed to migrate meta table "
307             + oldMetaDir.getName() + " to " + newMetaDir);
308       }
309     } else {
310       // on windows NTFS, meta's name is .META (note the missing dot at the end)
311       oldMetaDir = new Path(rootDir, ".META");
312       if (fs.exists(oldMetaDir)) {
313         LOG.info("Migrating meta table " + oldMetaDir.getName() + " to " + newMetaDir);
314         if (!fs.rename(oldMetaDir, newMetaDir)) {
315           throw new IOException("Failed to migrate meta table "
316               + oldMetaDir.getName() + " to " + newMetaDir);
317         }
318       }
319     }
320 
321     // Since meta table name has changed rename meta region dir from it's old encoding to new one
322     Path oldMetaRegionDir = new Path(rootDir, new Path(newMetaDir, "1028785192").toString());
323     if (fs.exists(oldMetaRegionDir)) {
324       LOG.info("Migrating meta region " + oldMetaRegionDir + " to " + newMetaRegionDir);
325       if (!fs.rename(oldMetaRegionDir, newMetaRegionDir)) {
326         throw new IOException("Failed to migrate meta region "
327             + oldMetaRegionDir + " to " + newMetaRegionDir);
328       }
329     }
330     // Remove .tableinfo files as they refer to ".META.".
331     // They will be recreated by master on startup.
332     removeTableInfoInPre96Format(TableName.META_TABLE_NAME);
333 
334     Path oldRootDir = new Path(rootDir, "-ROOT-");
335     if(!fs.rename(oldRootDir, backupDir)) {
336       throw new IllegalStateException("Failed to old data: "+oldRootDir+" to "+backupDir);
337     }
338   }
339 
340   /**
341    * Removes .tableinfo files that are laid in pre-96 format (i.e., the tableinfo files are under
342    * table directory).
343    * @param tableName
344    * @throws IOException
345    */
346   private void removeTableInfoInPre96Format(TableName tableName) throws IOException {
347     Path tableDir = FSUtils.getTableDir(rootDir, tableName);
348     FileStatus[] status = FSUtils.listStatus(fs, tableDir, TABLEINFO_PATHFILTER);
349     if (status == null) return;
350     for (FileStatus fStatus : status) {
351       FSUtils.delete(fs, fStatus.getPath(), false);
352     }
353   }
354 
355   public void migrateACL() throws IOException {
356 
357     TableName oldTableName = TableName.valueOf(OLD_ACL);
358     Path oldTablePath = new Path(rootDir, oldTableName.getNameAsString());
359 
360     if(!fs.exists(oldTablePath)) {
361       return;
362     }
363 
364     LOG.info("Migrating ACL table");
365 
366     TableName newTableName = AccessControlLists.ACL_TABLE_NAME;
367     Path newTablePath = FSUtils.getTableDir(rootDir, newTableName);
368     HTableDescriptor oldDesc =
369         readTableDescriptor(fs, getCurrentTableInfoStatus(fs, oldTablePath));
370 
371     if(FSTableDescriptors.getTableInfoPath(fs, newTablePath) == null) {
372       LOG.info("Creating new tableDesc for ACL");
373       HTableDescriptor newDesc = new HTableDescriptor(oldDesc);
374       newDesc.setName(newTableName);
375       new FSTableDescriptors(this.conf).createTableDescriptorForTableDirectory(
376         newTablePath, newDesc, true);
377     }
378 
379 
380     ServerName fakeServer = ServerName.valueOf("nsupgrade", 96, 123);
381     final WALFactory walFactory = new WALFactory(conf, null, fakeServer.toString());
382     WAL metawal = walFactory.getMetaWAL(HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes());
383     FSTableDescriptors fst = new FSTableDescriptors(conf);
384     HRegion meta = HRegion.openHRegion(rootDir, HRegionInfo.FIRST_META_REGIONINFO,
385         fst.get(TableName.META_TABLE_NAME), metawal, conf);
386     HRegion region = null;
387     try {
388       for(Path regionDir : FSUtils.getRegionDirs(fs, oldTablePath)) {
389         LOG.info("Migrating ACL region "+regionDir.getName());
390         HRegionInfo oldRegionInfo = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir);
391         HRegionInfo newRegionInfo =
392             new HRegionInfo(newTableName,
393                 oldRegionInfo.getStartKey(),
394                 oldRegionInfo.getEndKey(),
395                 oldRegionInfo.isSplit(),
396                 oldRegionInfo.getRegionId());
397         newRegionInfo.setOffline(oldRegionInfo.isOffline());
398         region =
399             new HRegion(
400                 HRegionFileSystem.openRegionFromFileSystem(conf, fs, oldTablePath,
401                     oldRegionInfo, false),
402                 metawal,
403                 conf,
404                 oldDesc,
405                 null);
406         region.initialize();
407         updateAcls(region);
408         // closing the region would flush it so we don't need an explicit flush to save
409         // acl changes.
410         region.close();
411 
412         //Create new region dir
413         Path newRegionDir = new Path(newTablePath, newRegionInfo.getEncodedName());
414         if(!fs.exists(newRegionDir)) {
415           if(!fs.mkdirs(newRegionDir)) {
416             throw new IllegalStateException("Failed to create new region dir: " + newRegionDir);
417           }
418         }
419 
420         //create new region info file, delete in case one exists
421         HRegionFileSystem.openRegionFromFileSystem(conf, fs, newTablePath, newRegionInfo, false);
422 
423         //migrate region contents
424         for(FileStatus file : fs.listStatus(regionDir, new FSUtils.UserTableDirFilter(fs)))  {
425           if(file.getPath().getName().equals(HRegionFileSystem.REGION_INFO_FILE))
426             continue;
427           if(!fs.rename(file.getPath(), newRegionDir))  {
428             throw new IllegalStateException("Failed to move file "+file.getPath()+" to " +
429                 newRegionDir);
430           }
431         }
432         meta.put(MetaTableAccessor.makePutFromRegionInfo(newRegionInfo));
433         meta.delete(MetaTableAccessor.makeDeleteFromRegionInfo(oldRegionInfo));
434       }
435     } finally {
436       meta.flush(true);
437       meta.waitForFlushesAndCompactions();
438       meta.close();
439       walFactory.close();
440       if(region != null) {
441         region.close();
442       }
443     }
444     if(!fs.rename(oldTablePath, backupDir)) {
445       throw new IllegalStateException("Failed to old data: "+oldTablePath+" to "+backupDir);
446     }
447   }
448 
449   /**
450    * Deletes the old _acl_ entry, and inserts a new one using namespace.
451    * @param region
452    * @throws IOException
453    */
454   void updateAcls(HRegion region) throws IOException {
455     byte[] rowKey = Bytes.toBytes(NamespaceUpgrade.OLD_ACL);
456     // get the old _acl_ entry, if present.
457     Get g = new Get(rowKey);
458     Result r = region.get(g);
459     if (r != null && r.size() > 0) {
460       // create a put for new _acl_ entry with rowkey as hbase:acl
461       Put p = new Put(AccessControlLists.ACL_GLOBAL_NAME);
462       for (Cell c : r.rawCells()) {
463         p.addImmutable(CellUtil.cloneFamily(c), CellUtil.cloneQualifier(c), CellUtil.cloneValue(c));
464       }
465       region.put(p);
466       // delete the old entry
467       Delete del = new Delete(rowKey);
468       region.delete(del);
469     }
470 
471     // delete the old entry for '-ROOT-'
472     rowKey = Bytes.toBytes(TableName.OLD_ROOT_STR);
473     Delete del = new Delete(rowKey);
474     region.delete(del);
475 
476     // rename .META. to hbase:meta
477     rowKey = Bytes.toBytes(TableName.OLD_META_STR);
478     g = new Get(rowKey);
479     r = region.get(g);
480     if (r != null && r.size() > 0) {
481       // create a put for new .META. entry with rowkey as hbase:meta
482       Put p = new Put(TableName.META_TABLE_NAME.getName());
483       for (Cell c : r.rawCells()) {
484         p.addImmutable(CellUtil.cloneFamily(c), CellUtil.cloneQualifier(c), CellUtil.cloneValue(c));
485       }
486       region.put(p);
487       // delete the old entry
488       del = new Delete(rowKey);
489       region.delete(del);
490     }
491   }
492 
493   //Culled from FSTableDescriptors
494   private static HTableDescriptor readTableDescriptor(FileSystem fs,
495                                                       FileStatus status) throws IOException {
496     int len = Ints.checkedCast(status.getLen());
497     byte [] content = new byte[len];
498     FSDataInputStream fsDataInputStream = fs.open(status.getPath());
499     try {
500       fsDataInputStream.readFully(content);
501     } finally {
502       fsDataInputStream.close();
503     }
504     HTableDescriptor htd = null;
505     try {
506       htd = HTableDescriptor.parseFrom(content);
507     } catch (DeserializationException e) {
508       throw new IOException("content=" + Bytes.toShort(content), e);
509     }
510     return htd;
511   }
512 
513   private static final PathFilter TABLEINFO_PATHFILTER = new PathFilter() {
514     @Override
515     public boolean accept(Path p) {
516       // Accept any file that starts with TABLEINFO_NAME
517       return p.getName().startsWith(".tableinfo");
518     }
519   };
520 
521   static final Comparator<FileStatus> TABLEINFO_FILESTATUS_COMPARATOR =
522   new Comparator<FileStatus>() {
523     @Override
524     public int compare(FileStatus left, FileStatus right) {
525       return right.compareTo(left);
526     }};
527 
528   // logic culled from FSTableDescriptors
529   static FileStatus getCurrentTableInfoStatus(FileSystem fs, Path dir)
530   throws IOException {
531     FileStatus [] status = FSUtils.listStatus(fs, dir, TABLEINFO_PATHFILTER);
532     if (status == null || status.length < 1) return null;
533     FileStatus mostCurrent = null;
534     for (FileStatus file : status) {
535       if (mostCurrent == null || TABLEINFO_FILESTATUS_COMPARATOR.compare(file, mostCurrent) < 0) {
536         mostCurrent = file;
537       }
538     }
539     return mostCurrent;
540   }
541 
542   public static boolean verifyNSUpgrade(FileSystem fs, Path rootDir)
543       throws IOException {
544     try {
545       return FSUtils.getVersion(fs, rootDir).equals(HConstants.FILE_SYSTEM_VERSION);
546     } catch (DeserializationException e) {
547       throw new IOException("Failed to verify namespace upgrade", e);
548     }
549   }
550 
551 
552   @Override
553   public int run(String[] args) throws Exception {
554     if (args.length < 1 || !args[0].equals("--upgrade")) {
555       System.out.println("Usage: <CMD> --upgrade");
556       return 0;
557     }
558     init();
559     upgradeTableDirs();
560     return 0;
561   }
562 
563   @Override
564   public void setConf(Configuration conf) {
565     this.conf = conf;
566   }
567 
568   @Override
569   public Configuration getConf() {
570     return conf;
571   }
572 }