blob: 35f68ad5bc53bed219e511194e1d40f52d1f1c82 [file] [log] [blame]
/*
* 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.accumulo.core.clientImpl;
import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.security.SecurityPermission;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.client.NamespaceNotFoundException;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.data.NamespaceId;
import org.apache.accumulo.core.data.TableId;
import org.apache.accumulo.core.manager.state.tables.TableState;
import org.apache.accumulo.core.metadata.MetadataTable;
import org.apache.accumulo.core.singletons.SingletonManager;
import org.apache.accumulo.core.singletons.SingletonService;
import org.apache.accumulo.core.util.Pair;
import org.apache.accumulo.fate.zookeeper.ZooCache;
import org.apache.accumulo.fate.zookeeper.ZooCacheFactory;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
public class Tables {
public static final String VALID_NAME_REGEX = "^(\\w+\\.)?(\\w+)$";
private static final SecurityPermission TABLES_PERMISSION =
new SecurityPermission("tablesPermission");
// Per instance cache will expire after 10 minutes in case we encounter an instance not used
// frequently
private static Cache<String,TableMap> instanceToMapCache =
CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build();
static {
SingletonManager.register(new SingletonService() {
boolean enabled = false;
@Override
public synchronized boolean isEnabled() {
return enabled;
}
@Override
public synchronized void enable() {
enabled = true;
}
@Override
public synchronized void disable() {
try {
instanceToMapCache.invalidateAll();
} finally {
enabled = false;
}
}
});
}
/**
* Lookup table ID in ZK. Throw TableNotFoundException if not found. Also wraps
* NamespaceNotFoundException in TableNotFoundException if namespace is not found.
*/
public static TableId getTableId(ClientContext context, String tableName)
throws TableNotFoundException {
try {
return _getTableId(context, tableName);
} catch (NamespaceNotFoundException e) {
throw new TableNotFoundException(tableName, e);
}
}
/**
* Return the cached ZooCache for provided context. ZooCache is initially created with a watcher
* that will clear the TableMap cache for that instance when WatchedEvent occurs.
*/
private static ZooCache getZooCache(final ClientContext context) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(TABLES_PERMISSION);
}
return new ZooCacheFactory().getZooCache(context.getZooKeepers(),
context.getZooKeepersSessionTimeOut());
}
/**
* Lookup table ID in ZK. If not found, clears cache and tries again.
*/
public static TableId _getTableId(ClientContext context, String tableName)
throws NamespaceNotFoundException, TableNotFoundException {
TableId tableId = getNameToIdMap(context).get(tableName);
if (tableId == null) {
// maybe the table exist, but the cache was not updated yet... so try to clear the cache and
// check again
clearCache(context);
tableId = getNameToIdMap(context).get(tableName);
if (tableId == null) {
String namespace = qualify(tableName).getFirst();
if (Namespaces.getNameToIdMap(context).containsKey(namespace))
throw new TableNotFoundException(null, tableName, null);
else
throw new NamespaceNotFoundException(null, namespace, null);
}
}
return tableId;
}
public static String getTableName(ClientContext context, TableId tableId)
throws TableNotFoundException {
String tableName = getIdToNameMap(context).get(tableId);
if (tableName == null)
throw new TableNotFoundException(tableId.canonical(), null, null);
return tableName;
}
public static String getTableOfflineMsg(ClientContext context, TableId tableId) {
if (tableId == null)
return "Table <unknown table> is offline";
try {
String tableName = Tables.getTableName(context, tableId);
return "Table " + tableName + " (" + tableId.canonical() + ") is offline";
} catch (TableNotFoundException e) {
return "Table <unknown table> (" + tableId.canonical() + ") is offline";
}
}
public static Map<String,TableId> getNameToIdMap(ClientContext context) {
return getTableMap(context).getNameToIdMap();
}
public static Map<TableId,String> getIdToNameMap(ClientContext context) {
return getTableMap(context).getIdtoNameMap();
}
/**
* Get the TableMap from the cache. A new one will be populated when needed. Cache is cleared
* manually by calling {@link #clearCache(ClientContext)}
*/
private static TableMap getTableMap(final ClientContext context) {
TableMap map;
final ZooCache zc = getZooCache(context);
map = getTableMap(context, zc);
if (!map.isCurrent(zc)) {
instanceToMapCache.invalidate(context.getInstanceID());
map = getTableMap(context, zc);
}
return map;
}
private static TableMap getTableMap(final ClientContext context, final ZooCache zc) {
try {
return instanceToMapCache.get(context.getInstanceID(), () -> new TableMap(context, zc));
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
public static boolean exists(ClientContext context, TableId tableId) {
ZooCache zc = getZooCache(context);
List<String> tableIds = zc.getChildren(context.getZooKeeperRoot() + Constants.ZTABLES);
return tableIds.contains(tableId.canonical());
}
public static void clearCache(ClientContext context) {
getZooCache(context).clear(context.getZooKeeperRoot() + Constants.ZTABLES);
getZooCache(context).clear(context.getZooKeeperRoot() + Constants.ZNAMESPACES);
instanceToMapCache.invalidate(context.getInstanceID());
}
/**
* Clears the zoo cache from instance/root/{PATH}
*
* @param context
* The Accumulo client context
* @param zooPath
* A zookeeper path
*/
public static void clearCacheByPath(ClientContext context, final String zooPath) {
String thePath = zooPath.startsWith("/") ? zooPath : "/" + zooPath;
getZooCache(context).clear(context.getZooKeeperRoot() + thePath);
instanceToMapCache.invalidate(context.getInstanceID());
}
public static String getPrintableTableInfoFromId(ClientContext context, TableId tableId) {
String tableName = null;
try {
tableName = getTableName(context, tableId);
} catch (TableNotFoundException e) {
// handled in the string formatting
}
return tableName == null ? String.format("?(ID:%s)", tableId.canonical())
: String.format("%s(ID:%s)", tableName, tableId.canonical());
}
public static String getPrintableTableInfoFromName(ClientContext context, String tableName) {
TableId tableId = null;
try {
tableId = getTableId(context, tableName);
} catch (TableNotFoundException e) {
// handled in the string formatting
}
return tableId == null ? String.format("%s(?)", tableName)
: String.format("%s(ID:%s)", tableName, tableId.canonical());
}
public static TableState getTableState(ClientContext context, TableId tableId) {
return getTableState(context, tableId, false);
}
/**
* Get the current state of the table using the tableid. The boolean clearCache, if true will
* clear the table state in zookeeper before fetching the state. Added with ACCUMULO-4574.
*
* @param context
* the Accumulo client context
* @param tableId
* the table id
* @param clearCachedState
* if true clear the table state in zookeeper before checking status
* @return the table state.
*/
public static TableState getTableState(ClientContext context, TableId tableId,
boolean clearCachedState) {
String statePath = context.getZooKeeperRoot() + Constants.ZTABLES + "/" + tableId.canonical()
+ Constants.ZTABLE_STATE;
if (clearCachedState) {
Tables.clearCacheByPath(context, statePath);
}
ZooCache zc = getZooCache(context);
byte[] state = zc.get(statePath);
if (state == null)
return TableState.UNKNOWN;
return TableState.valueOf(new String(state, UTF_8));
}
public static String qualified(String tableName) {
return qualified(tableName, Namespace.DEFAULT.name());
}
public static String qualified(String tableName, String defaultNamespace) {
Pair<String,String> qualifiedTableName = qualify(tableName, defaultNamespace);
if (Namespace.DEFAULT.name().equals(qualifiedTableName.getFirst()))
return qualifiedTableName.getSecond();
else
return qualifiedTableName.toString("", ".", "");
}
public static Pair<String,String> qualify(String tableName) {
return qualify(tableName, Namespace.DEFAULT.name());
}
public static Pair<String,String> qualify(String tableName, String defaultNamespace) {
if (!tableName.matches(VALID_NAME_REGEX))
throw new IllegalArgumentException("Invalid table name '" + tableName + "'");
if (MetadataTable.OLD_NAME.equals(tableName))
tableName = MetadataTable.NAME;
if (tableName.contains(".")) {
String[] s = tableName.split("\\.", 2);
return new Pair<>(s[0], s[1]);
}
return new Pair<>(defaultNamespace, tableName);
}
/**
* Returns the namespace id for a given table ID.
*
* @param context
* The Accumulo client context
* @param tableId
* The tableId
* @return The namespace id which this table resides in.
* @throws IllegalArgumentException
* if the table doesn't exist in ZooKeeper
*/
public static NamespaceId getNamespaceId(ClientContext context, TableId tableId)
throws TableNotFoundException {
checkArgument(context != null, "instance is null");
checkArgument(tableId != null, "tableId is null");
ZooCache zc = getZooCache(context);
byte[] n = zc.get(context.getZooKeeperRoot() + Constants.ZTABLES + "/" + tableId
+ Constants.ZTABLE_NAMESPACE);
// We might get null out of ZooCache if this tableID doesn't exist
if (n == null) {
throw new TableNotFoundException(tableId.canonical(), null, null);
}
return NamespaceId.of(new String(n, UTF_8));
}
}