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 */ 018package org.apache.hadoop.hbase.regionserver.storefiletracker; 019 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.Map; 023import org.apache.hadoop.conf.Configuration; 024import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 025import org.apache.hadoop.hbase.client.TableDescriptor; 026import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 027import org.apache.hadoop.hbase.procedure2.util.StringUtils; 028import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; 029import org.apache.hadoop.hbase.regionserver.StoreContext; 030import org.apache.hadoop.hbase.regionserver.StoreUtils; 031import org.apache.hadoop.hbase.util.ReflectionUtils; 032import org.apache.yetus.audience.InterfaceAudience; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 037 038/** 039 * Factory method for creating store file tracker. 040 * <p/> 041 * The current implementations are: 042 * <ul> 043 * <li><em>default</em>: DefaultStoreFileTracker, see {@link DefaultStoreFileTracker}.</li> 044 * <li><em>file</em>:FileBasedStoreFileTracker, see {@link FileBasedStoreFileTracker}.</li> 045 * <li><em>migration</em>:MigrationStoreFileTracker, see {@link MigrationStoreFileTracker}.</li> 046 * </ul> 047 * @see DefaultStoreFileTracker 048 * @see FileBasedStoreFileTracker 049 * @see MigrationStoreFileTracker 050 */ 051@InterfaceAudience.Private 052public final class StoreFileTrackerFactory { 053 054 private static final Logger LOG = LoggerFactory.getLogger(StoreFileTrackerFactory.class); 055 056 public static final String TRACKER_IMPL = "hbase.store.file-tracker.impl"; 057 058 /** 059 * Maps between configuration names for trackers and implementation classes. 060 */ 061 public enum Trackers { 062 DEFAULT(DefaultStoreFileTracker.class), 063 FILE(FileBasedStoreFileTracker.class), 064 MIGRATION(MigrationStoreFileTracker.class); 065 066 final Class<? extends StoreFileTracker> clazz; 067 068 Trackers(Class<? extends StoreFileTracker> clazz) { 069 this.clazz = clazz; 070 } 071 } 072 073 private static final Map<Class<? extends StoreFileTracker>, Trackers> CLASS_TO_ENUM = reverse(); 074 075 private static Map<Class<? extends StoreFileTracker>, Trackers> reverse() { 076 Map<Class<? extends StoreFileTracker>, Trackers> map = new HashMap<>(); 077 for (Trackers tracker : Trackers.values()) { 078 map.put(tracker.clazz, tracker); 079 } 080 return Collections.unmodifiableMap(map); 081 } 082 083 private StoreFileTrackerFactory() { 084 } 085 086 public static String getStoreFileTrackerName(Configuration conf) { 087 return conf.get(TRACKER_IMPL, Trackers.DEFAULT.name()); 088 } 089 090 public static String getStoreFileTrackerName(Class<? extends StoreFileTracker> clazz) { 091 Trackers name = CLASS_TO_ENUM.get(clazz); 092 return name != null ? name.name() : clazz.getName(); 093 } 094 095 public static Class<? extends StoreFileTracker> getTrackerClass(Configuration conf) { 096 try { 097 Trackers tracker = Trackers.valueOf(getStoreFileTrackerName(conf).toUpperCase()); 098 return tracker.clazz; 099 } catch (IllegalArgumentException e) { 100 // Fall back to them specifying a class name 101 return conf.getClass(TRACKER_IMPL, Trackers.DEFAULT.clazz, StoreFileTracker.class); 102 } 103 } 104 105 public static Class<? extends StoreFileTracker> getTrackerClass(String trackerNameOrClass) { 106 try { 107 Trackers tracker = Trackers.valueOf(trackerNameOrClass.toUpperCase()); 108 return tracker.clazz; 109 } catch (IllegalArgumentException e) { 110 // Fall back to them specifying a class name 111 try { 112 return Class.forName(trackerNameOrClass).asSubclass(StoreFileTracker.class); 113 } catch (ClassNotFoundException e1) { 114 throw new RuntimeException(e1); 115 } 116 } 117 } 118 119 public static StoreFileTracker create(Configuration conf, boolean isPrimaryReplica, 120 StoreContext ctx) { 121 Class<? extends StoreFileTracker> tracker = getTrackerClass(conf); 122 LOG.debug("instantiating StoreFileTracker impl {}", tracker.getName()); 123 return ReflectionUtils.newInstance(tracker, conf, isPrimaryReplica, ctx); 124 } 125 126 /** 127 * Used at master side when splitting/merging regions, as we do not have a Store, thus no 128 * StoreContext at master side. 129 */ 130 public static StoreFileTracker create(Configuration conf, TableDescriptor td, 131 ColumnFamilyDescriptor cfd, HRegionFileSystem regionFs) { 132 StoreContext ctx = 133 StoreContext.getBuilder().withColumnFamilyDescriptor(cfd).withRegionFileSystem(regionFs) 134 .withFamilyStoreDirectoryPath(regionFs.getStoreDir(cfd.getNameAsString())).build(); 135 return StoreFileTrackerFactory.create(mergeConfigurations(conf, td, cfd), true, ctx); 136 } 137 138 private static Configuration mergeConfigurations(Configuration global, TableDescriptor table, 139 ColumnFamilyDescriptor family) { 140 return StoreUtils.createStoreConfiguration(global, table, family); 141 } 142 143 static Class<? extends StoreFileTrackerBase> 144 getStoreFileTrackerClassForMigration(Configuration conf, String configName) { 145 String trackerName = 146 Preconditions.checkNotNull(conf.get(configName), "config %s is not set", configName); 147 try { 148 return Trackers.valueOf(trackerName.toUpperCase()).clazz 149 .asSubclass(StoreFileTrackerBase.class); 150 } catch (IllegalArgumentException e) { 151 // Fall back to them specifying a class name 152 try { 153 return Class.forName(trackerName).asSubclass(StoreFileTrackerBase.class); 154 } catch (ClassNotFoundException cnfe) { 155 throw new RuntimeException(cnfe); 156 } 157 } 158 } 159 160 /** 161 * Create store file tracker to be used as source or destination for 162 * {@link MigrationStoreFileTracker}. 163 */ 164 static StoreFileTrackerBase createForMigration(Configuration conf, String configName, 165 boolean isPrimaryReplica, StoreContext ctx) { 166 Class<? extends StoreFileTrackerBase> tracker = 167 getStoreFileTrackerClassForMigration(conf, configName); 168 // prevent nest of MigrationStoreFileTracker, it will cause infinite recursion. 169 if (MigrationStoreFileTracker.class.isAssignableFrom(tracker)) { 170 throw new IllegalArgumentException("Should not specify " + configName + " as " 171 + Trackers.MIGRATION + " because it can not be nested"); 172 } 173 LOG.debug("instantiating StoreFileTracker impl {} as {}", tracker.getName(), configName); 174 return ReflectionUtils.newInstance(tracker, conf, isPrimaryReplica, ctx); 175 } 176 177 public static TableDescriptor updateWithTrackerConfigs(Configuration conf, 178 TableDescriptor descriptor) { 179 // CreateTableProcedure needs to instantiate the configured SFT impl, in order to update table 180 // descriptors with the SFT impl specific configs. By the time this happens, the table has no 181 // regions nor stores yet, so it can't create a proper StoreContext. 182 if (StringUtils.isEmpty(descriptor.getValue(TRACKER_IMPL))) { 183 StoreFileTracker tracker = StoreFileTrackerFactory.create(conf, true, null); 184 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(descriptor); 185 return tracker.updateWithTrackerConfigs(builder).build(); 186 } 187 return descriptor; 188 } 189 190 public static boolean isMigration(Class<?> clazz) { 191 return MigrationStoreFileTracker.class.isAssignableFrom(clazz); 192 } 193}