org.apache.hadoop.hbase.regionserver.metrics.SchemaConfigured.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hbase.regionserver.metrics.SchemaConfigured.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.hadoop.hbase.regionserver.metrics;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.io.HeapSize;
import org.apache.hadoop.hbase.io.hfile.HFile;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.metrics.SchemaMetrics.SchemaAware;
import org.apache.hadoop.hbase.util.ClassSize;

/**
 * A base class for objects that are associated with a particular table and
 * column family. Provides a way to obtain the schema metrics object.
 * <p>
 * Due to the variety of things that can be associated with a table/CF, there
 * are many ways to initialize this base class, either in the constructor, or
 * from another similar object. For example, an HFile reader configures HFile
 * blocks it reads with its own table/CF name.
 */
public class SchemaConfigured implements HeapSize, SchemaAware {
    private static final Log LOG = LogFactory.getLog(SchemaConfigured.class);

    // These are not final because we set them at runtime e.g. for HFile blocks.
    private String cfName;
    private String tableName;

    /**
     * Schema metrics. Can only be initialized when we know our column family
     * name, table name, and have had a chance to take a look at the
     * configuration (in {@link SchemaMetrics#configureGlobally(Configuration))
     * so we know whether we are using per-table metrics. Therefore, initialized
     * lazily. We don't make this volatile because even if a thread sees a stale
     * value of null, it will be re-initialized to the same value that other
     * threads see.
     */
    private SchemaMetrics schemaMetrics;

    static {
        if (ClassSize.OBJECT <= 0 || ClassSize.REFERENCE <= 0) {
            throw new AssertionError("Class sizes are not initialized");
        }
    }

    /**
     * Estimated heap size of this object. We don't count table name and column
     * family name characters because these strings are shared among many
     * objects. We need unaligned size to reuse this in subclasses.
     */
    public static final int SCHEMA_CONFIGURED_UNALIGNED_HEAP_SIZE = ClassSize.OBJECT + 3 * ClassSize.REFERENCE;

    private static final int SCHEMA_CONFIGURED_ALIGNED_HEAP_SIZE = ClassSize
            .align(SCHEMA_CONFIGURED_UNALIGNED_HEAP_SIZE);

    /** A helper constructor that configures the "use table name" flag. */
    private SchemaConfigured(Configuration conf) {
        SchemaMetrics.configureGlobally(conf);
        // Even though we now know if table-level metrics are used, we can't
        // initialize schemaMetrics yet, because CF and table name are only known
        // to the calling constructor.
    }

    /**
     * Creates an instance corresponding to an unknown table and column family.
     * Used in unit tests. 
     */
    public static SchemaConfigured createUnknown() {
        return new SchemaConfigured(null, SchemaMetrics.UNKNOWN, SchemaMetrics.UNKNOWN);
    }

    /**
     * Default constructor. Only use when column/family name are not known at
     * construction (i.e. for HFile blocks).
     */
    public SchemaConfigured() {
    }

    /**
     * Initialize table and column family name from an HFile path. If
     * configuration is null,
     * {@link SchemaMetrics#configureGlobally(Configuration)} should have been
     * called already.
     */
    public SchemaConfigured(Configuration conf, Path path) {
        this(conf);

        if (path != null) {
            String splits[] = path.toString().split("/");
            int numPathLevels = splits.length;
            if (numPathLevels > 0 && splits[0].isEmpty()) {
                // This path starts with an '/'.
                --numPathLevels;
            }
            if (numPathLevels < HFile.MIN_NUM_HFILE_PATH_LEVELS) {
                LOG.warn("Could not determine table and column family of the HFile " + "path " + path
                        + ". Expecting at least " + HFile.MIN_NUM_HFILE_PATH_LEVELS + " path components.");
                path = null;
            } else {
                cfName = splits[splits.length - 2];
                if (cfName.equals(HRegion.REGION_TEMP_SUBDIR)) {
                    // This is probably a compaction or flush output file. We will set
                    // the real CF name later.
                    cfName = null;
                } else {
                    cfName = cfName.intern();
                }
                tableName = splits[splits.length - 4].intern();
                return;
            }
        }

        // This might also happen if we were passed an incorrect path.
        cfName = SchemaMetrics.UNKNOWN;
        tableName = SchemaMetrics.UNKNOWN;
    }

    /**
     * Used when we know an HFile path to deduce table and CF name from, but do
     * not have a configuration.
     * @param path an HFile path
     */
    public SchemaConfigured(Path path) {
        this(null, path);
    }

    /**
     * Used when we know table and column family name. If configuration is null,
     * {@link SchemaMetrics#configureGlobally(Configuration)} should have been
     * called already.
     */
    public SchemaConfigured(Configuration conf, String tableName, String cfName) {
        this(conf);
        this.tableName = tableName != null ? tableName.intern() : tableName;
        this.cfName = cfName != null ? cfName.intern() : cfName;
    }

    public SchemaConfigured(SchemaAware that) {
        tableName = that.getTableName().intern();
        cfName = that.getColumnFamilyName().intern();
        schemaMetrics = that.getSchemaMetrics();
    }

    @Override
    public String getTableName() {
        return tableName;
    }

    @Override
    public String getColumnFamilyName() {
        return cfName;
    }

    @Override
    public SchemaMetrics getSchemaMetrics() {
        if (schemaMetrics == null) {
            if (tableName == null || cfName == null) {
                throw new IllegalStateException(
                        "Schema metrics requested before " + "table/CF name initialization: " + schemaConfAsJSON());
            }
            schemaMetrics = SchemaMetrics.getInstance(tableName, cfName);
        }
        return schemaMetrics;
    }

    /**
     * Configures the given object (e.g. an HFile block) with the current table
     * and column family name, and the associated collection of metrics. Please
     * note that this method configures the <b>other</b> object, not <b>this</b>
     * object.
     */
    public void passSchemaMetricsTo(SchemaConfigured target) {
        if (isNull()) {
            resetSchemaMetricsConf(target);
            return;
        }

        if (!isSchemaConfigured()) {
            // Cannot configure another object if we are not configured ourselves.
            throw new IllegalStateException("Table name/CF not initialized: " + schemaConfAsJSON());
        }

        if (conflictingWith(target)) {
            // Make sure we don't try to change table or CF name.
            throw new IllegalArgumentException("Trying to change table name to \"" + tableName + "\", CF name to \""
                    + cfName + "\" from " + target.schemaConfAsJSON());
        }

        target.tableName = tableName.intern();
        target.cfName = cfName.intern();
        target.schemaMetrics = schemaMetrics;
        target.schemaConfigurationChanged();
    }

    /**
     * Reset schema metrics configuration in this particular instance. Used when
     * legitimately need to re-initialize the object with another table/CF.
     * This is a static method because its use is discouraged and reserved for
     * when it is really necessary (e.g. writing HFiles in a temp direcdtory
     * on compaction).
     */
    public static void resetSchemaMetricsConf(SchemaConfigured target) {
        target.tableName = null;
        target.cfName = null;
        target.schemaMetrics = null;
        target.schemaConfigurationChanged();
    }

    @Override
    public long heapSize() {
        return SCHEMA_CONFIGURED_ALIGNED_HEAP_SIZE;
    }

    public String schemaConfAsJSON() {
        return "{\"tableName\":\"" + tableName + "\",\"cfName\":\"" + cfName + "\"}";
    }

    protected boolean isSchemaConfigured() {
        return tableName != null && cfName != null;
    }

    private boolean isNull() {
        return tableName == null && cfName == null && schemaMetrics == null;
    }

    /**
     * Determines if the current object's table/CF settings are not in conflict
     * with the other object's table and CF. If the other object's table/CF are
     * undefined, they are not considered to be in conflict. Used to sanity-check
     * configuring the other object with this object's table/CF.
     */
    boolean conflictingWith(SchemaConfigured other) {
        return (other.tableName != null && !tableName.equals(other.tableName))
                || (other.cfName != null && !cfName.equals(other.cfName));
    }

    /**
     * A hook method called when schema configuration changes. Can be used to
     * update schema-aware member fields.
     */
    protected void schemaConfigurationChanged() {
    }

}