/* * 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.client.impl; import static com.google.common.base.Preconditions.checkArgument; import static java.nio.charset.StandardCharsets.UTF_8; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicLong; import org.apache.accumulo.core.Constants; import org.apache.accumulo.core.client.Instance; import org.apache.accumulo.core.client.NamespaceNotFoundException; import org.apache.accumulo.core.client.TableNotFoundException; import org.apache.accumulo.core.master.state.tables.TableState; import org.apache.accumulo.core.metadata.MetadataTable; import org.apache.accumulo.core.util.Pair; import org.apache.accumulo.core.zookeeper.ZooUtil; import org.apache.accumulo.fate.zookeeper.ZooCache; import org.apache.accumulo.fate.zookeeper.ZooCacheFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Tables { private static final Logger log = LoggerFactory.getLogger(Tables.class); public static final String VALID_NAME_REGEX = "^(\\w+\\.)?(\\w+)$"; private static final AtomicLong cacheResetCount = new AtomicLong(0); private static ZooCache getZooCache(Instance instance) { return new ZooCacheFactory().getZooCache(instance.getZooKeepers(), instance.getZooKeepersSessionTimeOut()); } private static SortedMap<String,String> getMap(Instance instance, boolean nameAsKey) { ZooCache zc = getZooCache(instance); List<String> tableIds = zc.getChildren(ZooUtil.getRoot(instance) + Constants.ZTABLES); TreeMap<String,String> tableMap = new TreeMap<>(); Map<String,String> namespaceIdToNameMap = new HashMap<>(); for (String tableId : tableIds) { byte[] tableName = zc.get(ZooUtil.getRoot(instance) + Constants.ZTABLES + "/" + tableId + Constants.ZTABLE_NAME); byte[] nId = zc.get(ZooUtil.getRoot(instance) + Constants.ZTABLES + "/" + tableId + Constants.ZTABLE_NAMESPACE); String namespaceName = Namespaces.DEFAULT_NAMESPACE; // create fully qualified table name if (nId == null) { namespaceName = null; } else { String namespaceId = new String(nId, UTF_8); if (!namespaceId.equals(Namespaces.DEFAULT_NAMESPACE_ID)) { try { namespaceName = namespaceIdToNameMap.get(namespaceId); if (namespaceName == null) { namespaceName = Namespaces.getNamespaceName(instance, namespaceId); namespaceIdToNameMap.put(namespaceId, namespaceName); } } catch (NamespaceNotFoundException e) { log.error("Table (" + tableId + ") contains reference to namespace (" + namespaceId + ") that doesn't exist", e); continue; } } } if (tableName != null && namespaceName != null) { String tableNameStr = qualified(new String(tableName, UTF_8), namespaceName); if (nameAsKey) tableMap.put(tableNameStr, tableId); else tableMap.put(tableId, tableNameStr); } } return tableMap; } public static String getTableId(Instance instance, String tableName) throws TableNotFoundException { try { return _getTableId(instance, tableName); } catch (NamespaceNotFoundException e) { throw new TableNotFoundException(tableName, e); } } public static String _getTableId(Instance instance, String tableName) throws NamespaceNotFoundException, TableNotFoundException { String tableId = getNameToIdMap(instance).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(instance); tableId = getNameToIdMap(instance).get(tableName); if (tableId == null) { String namespace = qualify(tableName).getFirst(); if (Namespaces.getNameToIdMap(instance).containsKey(namespace)) throw new TableNotFoundException(null, tableName, null); else throw new NamespaceNotFoundException(null, namespace, null); } } return tableId; } public static String getTableName(Instance instance, String tableId) throws TableNotFoundException { String tableName = getIdToNameMap(instance).get(tableId); if (tableName == null) throw new TableNotFoundException(tableId, null, null); return tableName; } public static SortedMap<String,String> getNameToIdMap(Instance instance) { return getMap(instance, true); } public static SortedMap<String,String> getIdToNameMap(Instance instance) { return getMap(instance, false); } public static boolean exists(Instance instance, String tableId) { ZooCache zc = getZooCache(instance); List<String> tableIds = zc.getChildren(ZooUtil.getRoot(instance) + Constants.ZTABLES); return tableIds.contains(tableId); } public static void clearCache(Instance instance) { cacheResetCount.incrementAndGet(); getZooCache(instance).clear(ZooUtil.getRoot(instance) + Constants.ZTABLES); getZooCache(instance).clear(ZooUtil.getRoot(instance) + Constants.ZNAMESPACES); } /** * Clears the zoo cache from instance/root/{PATH} * * @param instance * The Accumulo Instance * @param zooPath * A zookeeper path */ public static void clearCacheByPath(Instance instance, final String zooPath) { String thePath; if (zooPath.startsWith("/")) { thePath = zooPath; } else { thePath = "/" + zooPath; } getZooCache(instance).clear(ZooUtil.getRoot(instance) + thePath); } public static String getPrintableTableNameFromId(Map<String,String> tidToNameMap, String tableId) { String tableName = tidToNameMap.get(tableId); return tableName == null ? "(ID:" + tableId + ")" : tableName; } public static String getPrintableTableInfoFromId(Instance instance, String tableId) { String tableName = null; try { tableName = getTableName(instance, tableId); } catch (TableNotFoundException e) { // handled in the string formatting } return tableName == null ? String.format("?(ID:%s)", tableId) : String.format("%s(ID:%s)", tableName, tableId); } public static String getPrintableTableInfoFromName(Instance instance, String tableName) { String tableId = null; try { tableId = getTableId(instance, tableName); } catch (TableNotFoundException e) { // handled in the string formatting } return tableId == null ? String.format("%s(?)", tableName) : String.format("%s(ID:%s)", tableName, tableId); } public static TableState getTableState(Instance instance, String tableId) { return getTableState(instance, 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 instance * the Accumulo instance. * @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(Instance instance, String tableId, boolean clearCachedState) { String statePath = ZooUtil.getRoot(instance) + Constants.ZTABLES + "/" + tableId + Constants.ZTABLE_STATE; if (clearCachedState) { Tables.clearCacheByPath(instance, statePath); } ZooCache zc = getZooCache(instance); byte[] state = zc.get(statePath); if (state == null) return TableState.UNKNOWN; return TableState.valueOf(new String(state, UTF_8)); } public static long getCacheResetCount() { return cacheResetCount.get(); } public static String qualified(String tableName) { return qualified(tableName, Namespaces.DEFAULT_NAMESPACE); } public static String qualified(String tableName, String defaultNamespace) { Pair<String,String> qualifiedTableName = qualify(tableName, defaultNamespace); if (Namespaces.DEFAULT_NAMESPACE.equals(qualifiedTableName.getFirst())) return qualifiedTableName.getSecond(); else return qualifiedTableName.toString("", ".", ""); } public static Pair<String,String> qualify(String tableName) { return qualify(tableName, Namespaces.DEFAULT_NAMESPACE); } 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 instance * The Accumulo Instance * @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 String getNamespaceId(Instance instance, String tableId) throws TableNotFoundException { checkArgument(instance != null, "instance is null"); checkArgument(tableId != null, "tableId is null"); ZooCache zc = getZooCache(instance); byte[] n = zc.get(ZooUtil.getRoot(instance) + Constants.ZTABLES + "/" + tableId + Constants.ZTABLE_NAMESPACE); // We might get null out of ZooCache if this tableID doesn't exist if (null == n) { throw new TableNotFoundException(tableId, null, null); } return new String(n, UTF_8); } }