/* * 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.server.conf; import java.util.HashMap; import java.util.Map; import org.apache.accumulo.core.client.Instance; import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.conf.AccumuloConfiguration; import org.apache.accumulo.core.conf.ConfigSanityCheck; import org.apache.accumulo.core.conf.DefaultConfiguration; import org.apache.accumulo.core.conf.SiteConfiguration; import org.apache.accumulo.fate.zookeeper.ZooCacheFactory; /** * A factor for configurations used by a server process. Instance of this class are thread-safe. */ public class ServerConfigurationFactory extends ServerConfiguration { private static final Map<String,Map<String,TableConfiguration>> tableConfigs = new HashMap<>(1); private static final Map<String,Map<String,NamespaceConfiguration>> namespaceConfigs = new HashMap<>(1); private static final Map<String,Map<String,NamespaceConfiguration>> tableParentConfigs = new HashMap<>(1); private static void addInstanceToCaches(String iid) { synchronized (tableConfigs) { tableConfigs.computeIfAbsent(iid, k -> new HashMap<>()); } synchronized (namespaceConfigs) { namespaceConfigs.computeIfAbsent(iid, k -> new HashMap<>()); } synchronized (tableParentConfigs) { tableParentConfigs.computeIfAbsent(iid, k -> new HashMap<>()); } } static boolean removeCachedTableConfiguration(String instanceId, String tableId) { synchronized (tableConfigs) { return tableConfigs.get(instanceId).remove(tableId) != null; } } static boolean removeCachedNamespaceConfiguration(String instanceId, String namespaceId) { synchronized (namespaceConfigs) { return namespaceConfigs.get(instanceId).remove(namespaceId) != null; } } static void clearCachedConfigurations() { synchronized (tableConfigs) { tableConfigs.clear(); } synchronized (namespaceConfigs) { namespaceConfigs.clear(); } synchronized (tableParentConfigs) { tableParentConfigs.clear(); } } static void expireAllTableObservers() { synchronized (tableConfigs) { for (Map<String,TableConfiguration> instanceMap : tableConfigs.values()) { for (TableConfiguration c : instanceMap.values()) { c.expireAllObservers(); } } } } private final Instance instance; private final String instanceID; private ZooCacheFactory zcf = new ZooCacheFactory(); public ServerConfigurationFactory(Instance instance) { this.instance = instance; instanceID = instance.getInstanceID(); addInstanceToCaches(instanceID); } void setZooCacheFactory(ZooCacheFactory zcf) { this.zcf = zcf; } private SiteConfiguration siteConfig = null; private DefaultConfiguration defaultConfig = null; private AccumuloConfiguration systemConfig = null; public synchronized SiteConfiguration getSiteConfiguration() { if (siteConfig == null) { siteConfig = SiteConfiguration.getInstance(getDefaultConfiguration()); } return siteConfig; } public synchronized DefaultConfiguration getDefaultConfiguration() { if (defaultConfig == null) { defaultConfig = DefaultConfiguration.getInstance(); } return defaultConfig; } @Override public synchronized AccumuloConfiguration getSystemConfiguration() { if (systemConfig == null) { systemConfig = new ZooConfigurationFactory().getInstance(instance, zcf, getSiteConfiguration()); } return systemConfig; } @Override public TableConfiguration getTableConfiguration(String tableId) { TableConfiguration conf; synchronized (tableConfigs) { conf = tableConfigs.get(instanceID).get(tableId); } // Can't hold the lock during the construction and validation of the config, // which would result in creating multiple objects for the same id. // // ACCUMULO-3859 We _cannot_ allow multiple instances to be created for a table. If the TableConfiguration // instance a Tablet holds is not the same as the one cached here, any ConfigurationObservers that // Tablet sets will never see updates from ZooKeeper which means that things like constraints and // default visibility labels will never be updated in a Tablet until it is reloaded. if (conf == null && Tables.exists(instance, tableId)) { conf = new TableConfiguration(instance, tableId, getNamespaceConfigurationForTable(tableId)); ConfigSanityCheck.validate(conf); synchronized (tableConfigs) { Map<String,TableConfiguration> configs = tableConfigs.get(instanceID); TableConfiguration existingConf = configs.get(tableId); if (null == existingConf) { // Configuration doesn't exist yet configs.put(tableId, conf); } else { // Someone beat us to the punch, reuse their instance instead of replacing it conf = existingConf; } } } return conf; } public NamespaceConfiguration getNamespaceConfigurationForTable(String tableId) { NamespaceConfiguration conf; synchronized (tableParentConfigs) { conf = tableParentConfigs.get(instanceID).get(tableId); } // can't hold the lock during the construction and validation of the config, // which may result in creating multiple objects for the same id, but that's ok. if (conf == null) { // changed - include instance in constructor call conf = new TableParentConfiguration(tableId, instance, getSystemConfiguration()); ConfigSanityCheck.validate(conf); synchronized (tableParentConfigs) { tableParentConfigs.get(instanceID).put(tableId, conf); } } return conf; } @Override public NamespaceConfiguration getNamespaceConfiguration(String namespaceId) { NamespaceConfiguration conf; // can't hold the lock during the construction and validation of the config, // which may result in creating multiple objects for the same id, but that's ok. synchronized (namespaceConfigs) { conf = namespaceConfigs.get(instanceID).get(namespaceId); } if (conf == null) { // changed - include instance in constructor call conf = new NamespaceConfiguration(namespaceId, instance, getSystemConfiguration()); conf.setZooCacheFactory(zcf); ConfigSanityCheck.validate(conf); synchronized (namespaceConfigs) { namespaceConfigs.get(instanceID).put(namespaceId, conf); } } return conf; } @Override public Instance getInstance() { return instance; } }