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    return create(conf, td, cfd, regionFs, true);
133  }
134
135  public static StoreFileTracker create(Configuration conf, TableDescriptor td,
136    ColumnFamilyDescriptor cfd, HRegionFileSystem regionFs, boolean isPrimaryReplica) {
137    StoreContext ctx =
138      StoreContext.getBuilder().withColumnFamilyDescriptor(cfd).withRegionFileSystem(regionFs)
139        .withFamilyStoreDirectoryPath(regionFs.getStoreDir(cfd.getNameAsString())).build();
140    return StoreFileTrackerFactory.create(mergeConfigurations(conf, td, cfd), isPrimaryReplica,
141      ctx);
142  }
143
144  private static Configuration mergeConfigurations(Configuration global, TableDescriptor table,
145    ColumnFamilyDescriptor family) {
146    return StoreUtils.createStoreConfiguration(global, table, family);
147  }
148
149  static Class<? extends StoreFileTrackerBase>
150    getStoreFileTrackerClassForMigration(Configuration conf, String configName) {
151    String trackerName =
152      Preconditions.checkNotNull(conf.get(configName), "config %s is not set", configName);
153    try {
154      return Trackers.valueOf(trackerName.toUpperCase()).clazz
155        .asSubclass(StoreFileTrackerBase.class);
156    } catch (IllegalArgumentException e) {
157      // Fall back to them specifying a class name
158      try {
159        return Class.forName(trackerName).asSubclass(StoreFileTrackerBase.class);
160      } catch (ClassNotFoundException cnfe) {
161        throw new RuntimeException(cnfe);
162      }
163    }
164  }
165
166  /**
167   * Create store file tracker to be used as source or destination for
168   * {@link MigrationStoreFileTracker}.
169   */
170  static StoreFileTrackerBase createForMigration(Configuration conf, String configName,
171    boolean isPrimaryReplica, StoreContext ctx) {
172    Class<? extends StoreFileTrackerBase> tracker =
173      getStoreFileTrackerClassForMigration(conf, configName);
174    // prevent nest of MigrationStoreFileTracker, it will cause infinite recursion.
175    if (MigrationStoreFileTracker.class.isAssignableFrom(tracker)) {
176      throw new IllegalArgumentException("Should not specify " + configName + " as "
177        + Trackers.MIGRATION + " because it can not be nested");
178    }
179    LOG.debug("instantiating StoreFileTracker impl {} as {}", tracker.getName(), configName);
180    return ReflectionUtils.newInstance(tracker, conf, isPrimaryReplica, ctx);
181  }
182
183  public static TableDescriptor updateWithTrackerConfigs(Configuration conf,
184    TableDescriptor descriptor) {
185    // CreateTableProcedure needs to instantiate the configured SFT impl, in order to update table
186    // descriptors with the SFT impl specific configs. By the time this happens, the table has no
187    // regions nor stores yet, so it can't create a proper StoreContext.
188    if (StringUtils.isEmpty(descriptor.getValue(TRACKER_IMPL))) {
189      StoreFileTracker tracker = StoreFileTrackerFactory.create(conf, true, null);
190      TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(descriptor);
191      return tracker.updateWithTrackerConfigs(builder).build();
192    }
193    return descriptor;
194  }
195
196  public static boolean isMigration(Class<?> clazz) {
197    return MigrationStoreFileTracker.class.isAssignableFrom(clazz);
198  }
199}