/* * 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.cassandra.config; import java.util.*; import com.google.common.base.Objects; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.locator.*; import org.apache.cassandra.service.StorageService; public final class KSMetaData { public final String name; public final Class strategyClass; public final Map strategyOptions; private final Map cfMetaData; public final boolean durableWrites; public final UTMetaData userTypes; public KSMetaData(String name, Class strategyClass, Map strategyOptions, boolean durableWrites) { this(name, strategyClass, strategyOptions, durableWrites, Collections.emptyList(), new UTMetaData()); } public KSMetaData(String name, Class strategyClass, Map strategyOptions, boolean durableWrites, Iterable cfDefs) { this(name, strategyClass, strategyOptions, durableWrites, cfDefs, new UTMetaData()); } private KSMetaData(String name, Class strategyClass, Map strategyOptions, boolean durableWrites, Iterable cfDefs, UTMetaData userTypes) { this.name = name; this.strategyClass = strategyClass == null ? NetworkTopologyStrategy.class : strategyClass; this.strategyOptions = strategyOptions; Map cfmap = new HashMap<>(); for (CFMetaData cfm : cfDefs) cfmap.put(cfm.cfName, cfm); this.cfMetaData = Collections.unmodifiableMap(cfmap); this.durableWrites = durableWrites; this.userTypes = userTypes; } // For new user created keyspaces (through CQL) public static KSMetaData newKeyspace(String name, String strategyName, Map options, boolean durableWrites) throws ConfigurationException { Class cls = AbstractReplicationStrategy.getClass(strategyName); if (cls.equals(LocalStrategy.class)) throw new ConfigurationException("Unable to use given strategy class: LocalStrategy is reserved for internal use."); return newKeyspace(name, cls, options, durableWrites, Collections.emptyList()); } public static KSMetaData newKeyspace(String name, Class strategyClass, Map options, boolean durablesWrites, Iterable cfDefs) { return new KSMetaData(name, strategyClass, options, durablesWrites, cfDefs, new UTMetaData()); } public KSMetaData cloneWithTableRemoved(CFMetaData table) { // clone ksm but do not include the new table List newTables = new ArrayList<>(cfMetaData().values()); newTables.remove(table); assert newTables.size() == cfMetaData().size() - 1; return cloneWith(newTables, userTypes); } public KSMetaData cloneWithTableAdded(CFMetaData table) { // clone ksm but include the new table List newTables = new ArrayList<>(cfMetaData().values()); newTables.add(table); assert newTables.size() == cfMetaData().size() + 1; return cloneWith(newTables, userTypes); } public KSMetaData cloneWith(Iterable tables, UTMetaData types) { return new KSMetaData(name, strategyClass, strategyOptions, durableWrites, tables, types); } public static KSMetaData testMetadata(String name, Class strategyClass, Map strategyOptions, CFMetaData... cfDefs) { return new KSMetaData(name, strategyClass, strategyOptions, true, Arrays.asList(cfDefs)); } public static KSMetaData testMetadataNotDurable(String name, Class strategyClass, Map strategyOptions, CFMetaData... cfDefs) { return new KSMetaData(name, strategyClass, strategyOptions, false, Arrays.asList(cfDefs)); } @Override public int hashCode() { return Objects.hashCode(name, strategyClass, strategyOptions, cfMetaData, durableWrites, userTypes); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof KSMetaData)) return false; KSMetaData other = (KSMetaData) o; return Objects.equal(name, other.name) && Objects.equal(strategyClass, other.strategyClass) && Objects.equal(strategyOptions, other.strategyOptions) && Objects.equal(cfMetaData, other.cfMetaData) && Objects.equal(durableWrites, other.durableWrites) && Objects.equal(userTypes, other.userTypes); } public Map cfMetaData() { return cfMetaData; } @Override public String toString() { return Objects.toStringHelper(this) .add("name", name) .add("strategyClass", strategyClass.getSimpleName()) .add("strategyOptions", strategyOptions) .add("cfMetaData", cfMetaData) .add("durableWrites", durableWrites) .add("userTypes", userTypes) .toString(); } public static Map optsWithRF(final Integer rf) { return Collections.singletonMap("replication_factor", rf.toString()); } public KSMetaData validate() throws ConfigurationException { if (!CFMetaData.isNameValid(name)) throw new ConfigurationException(String.format("Keyspace name must not be empty, more than %s characters long, or contain non-alphanumeric-underscore characters (got \"%s\")", Schema.NAME_LENGTH, name)); // Attempt to instantiate the ARS, which will throw a ConfigException if the strategy_options aren't fully formed TokenMetadata tmd = StorageService.instance.getTokenMetadata(); IEndpointSnitch eps = DatabaseDescriptor.getEndpointSnitch(); AbstractReplicationStrategy.validateReplicationStrategy(name, strategyClass, tmd, eps, strategyOptions); for (CFMetaData cfm : cfMetaData.values()) cfm.validate(); return this; } }