/*
* Copyright 2010 Outerthought bvba
*
* Licensed 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.lilyproject.util.hbase;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableExistsException;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.apache.hadoop.hbase.io.hfile.Compression;
import org.apache.hadoop.hbase.regionserver.StoreFile;
import org.apache.hadoop.hbase.util.Bytes;
public class HBaseTableFactoryImpl implements HBaseTableFactory {
private Log log = LogFactory.getLog(getClass());
private final Configuration configuration;
private final List<TableConfigEntry> tableConfigs;
private final ColumnFamilyConfig defaultCfConfig;
public HBaseTableFactoryImpl(Configuration configuration) {
this(configuration, null, new ColumnFamilyConfig());
}
public HBaseTableFactoryImpl(Configuration configuration, List<TableConfigEntry> tableConfigs,
ColumnFamilyConfig defaultCfConfig) {
this.configuration = configuration;
this.tableConfigs = tableConfigs == null ? Collections.<TableConfigEntry>emptyList() : tableConfigs;
this.defaultCfConfig = defaultCfConfig;
}
@Override
public HTableInterface getTable(HTableDescriptor tableDescriptor) throws IOException, InterruptedException {
return getTable(tableDescriptor, true);
}
@Override
public HTableInterface getTable(HTableDescriptor tableDescriptor, byte[][] splitKeys) throws IOException, InterruptedException {
return getTable(tableDescriptor, splitKeys, true);
}
@Override
public HTableInterface getTable(HTableDescriptor tableDescriptor, boolean create) throws IOException, InterruptedException {
return getTable(tableDescriptor, getSplitKeys(tableDescriptor.getName()), create);
}
private HTableInterface getTable(HTableDescriptor tableDescriptor, byte[][] splitKeys, boolean create) throws IOException, InterruptedException {
HBaseAdmin admin = new HBaseAdmin(configuration);
try {
try {
admin.getTableDescriptor(tableDescriptor.getName());
} catch (TableNotFoundException e) {
if (!create) {
throw e;
}
try {
// Make a deep copy, we don't want to touch the original
tableDescriptor = new HTableDescriptor(tableDescriptor);
configure(tableDescriptor);
int regionCount = splitKeys == null ? 1 : splitKeys.length + 1;
log.info("Creating '" + tableDescriptor.getNameAsString() + "' table using "
+ regionCount + " initial region" + (regionCount > 1 ? "s." : "."));
admin.createTable(tableDescriptor, splitKeys);
} catch (TableExistsException e2) {
// Table is meanwhile created by another process
log.info("Table already existed: '" + tableDescriptor.getNameAsString() + "'.");
}
}
//In all cases we need to wait until the table is available
// https://issues.apache.org/jira/browse/HBASE-6576
long startWait = System.currentTimeMillis();
long timeoutMillis = 2 * 60 * 1000L; // two minutes
while (!admin.isTableAvailable(tableDescriptor.getName())) {
if (System.currentTimeMillis() - startWait > timeoutMillis) {
throw new IOException("Timed out waiting for table " + Bytes.toStringBinary(tableDescriptor.getName()));
}
Thread.sleep(200);
}
// TODO we could check if the existing table matches the given table descriptor
return new LocalHTable(configuration, tableDescriptor.getName());
} finally {
admin.close();
}
}
@Override
public void configure(HTableDescriptor tableDescriptor) {
TableConfig tableConfig = getTableConfig(tableDescriptor.getName());
// max file size
Long maxFileSize = tableConfig.getMaxFileSize();
if (maxFileSize != null) {
tableDescriptor.setMaxFileSize(maxFileSize);
}
// memstore flush size
Long memStoreFlushSize = tableConfig.getMemStoreFlushSize();
if (memStoreFlushSize != null) {
tableDescriptor.setMemStoreFlushSize(memStoreFlushSize);
}
for (HColumnDescriptor column : tableDescriptor.getColumnFamilies()) {
ColumnFamilyConfig cfConf = tableConfig.getColumnFamilyConfig(column.getNameAsString());
// compression
Compression.Algorithm compression = cfConf.getCompression() != null ?
cfConf.getCompression() : defaultCfConfig.getCompression();
if (compression != null) {
column.setCompressionType(compression);
}
// block size
Integer blockSize = cfConf.getBlockSize() != null ?
cfConf.getBlockSize() : defaultCfConfig.getBlockSize();
if (blockSize != null) {
column.setBlocksize(blockSize);
}
// bloom filter
StoreFile.BloomType bloomFilter = cfConf.getBoomFilter() != null ?
cfConf.getBoomFilter() : defaultCfConfig.getBoomFilter();
if (bloomFilter != null) {
column.setBloomFilterType(bloomFilter);
}
}
}
@Override
public TableConfig getTableConfig(byte[] tableName) {
String tableNameString = Bytes.toString(tableName);
for (TableConfigEntry entry : tableConfigs) {
if (entry.matches(tableNameString)) {
log.debug("Table configuration settings: table \"" + tableNameString + "\" matched regex \""
+ entry.getTableNamePattern().pattern() + "\"");
return entry.getTableConfig();
}
}
log.debug("Table configuration settings: table \"" + tableNameString + "\" has no matching settings.");
return new TableConfig();
}
@Override
public byte[][] getSplitKeys(byte[] tableName) {
return getTableConfig(tableName).getSplitKeys();
}
}