Java tutorial
// Copyright 2018 JanusGraph Authors // // 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.janusgraph.diskstorage.configuration.builder; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.janusgraph.core.JanusGraphConfigurationException; import org.janusgraph.core.JanusGraphException; import org.janusgraph.diskstorage.Backend; import org.janusgraph.diskstorage.configuration.*; import org.janusgraph.diskstorage.configuration.backend.KCVSConfiguration; import org.janusgraph.diskstorage.keycolumnvalue.KeyColumnValueStoreManager; import org.janusgraph.diskstorage.keycolumnvalue.StoreFeatures; import org.janusgraph.diskstorage.util.time.TimestampProviders; import org.janusgraph.graphdb.configuration.JanusGraphConstants; import org.janusgraph.graphdb.configuration.validator.CompatibilityValidator; import org.janusgraph.graphdb.database.management.ManagementSystem; import org.janusgraph.util.system.LoggerUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; import java.util.Objects; import java.util.Set; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.*; /** * Builder to build {@link ReadConfiguration} instance of global configuration */ public class ReadConfigurationBuilder { private static final Logger log = LoggerFactory.getLogger(ReadConfigurationBuilder.class); private static final String BACKLEVEL_STORAGE_VERSION_EXCEPTION = "The storage version on the client or server is lower than the storage version of the graph: graph storage version %s vs. client storage version %s when opening graph %s."; private static final String INCOMPATIBLE_STORAGE_VERSION_EXCEPTION = "Storage version is incompatible with current client: graph storage version %s vs. client storage version %s when opening graph %s."; public ReadConfiguration buildGlobalConfiguration(ReadConfiguration localConfig, BasicConfiguration localBasicConfiguration, ModifiableConfiguration overwrite, KeyColumnValueStoreManager storeManager, ModifiableConfigurationBuilder modifiableConfigurationBuilder) { //Read out global configuration try (KCVSConfiguration keyColumnValueStoreConfiguration = Backend .getStandaloneGlobalConfiguration(storeManager, localBasicConfiguration)) { // If lock prefix is unspecified, specify it now if (!localBasicConfiguration.has(LOCK_LOCAL_MEDIATOR_GROUP)) { overwrite.set(LOCK_LOCAL_MEDIATOR_GROUP, storeManager.getName()); } //Freeze global configuration if not already frozen! ModifiableConfiguration globalWrite = modifiableConfigurationBuilder .buildGlobalWrite(keyColumnValueStoreConfiguration); if (!globalWrite.isFrozen()) { //Copy over global configurations globalWrite.setAll(getGlobalSubset(localBasicConfiguration.getAll())); setupJanusGraphVersion(globalWrite); setupStorageVersion(globalWrite); setupTimestampProvider(globalWrite, localBasicConfiguration, storeManager); globalWrite.freezeConfiguration(); } else { String graphName = localConfig.get(GRAPH_NAME.toStringWithoutRoot(), String.class); final boolean upgradeAllowed = isUpgradeAllowed(globalWrite, localBasicConfiguration); if (upgradeAllowed) { setupUpgradeConfiguration(graphName, globalWrite); } else { checkJanusGraphStorageVersionEquality(globalWrite, graphName); } checkJanusGraphVersion(globalWrite, localBasicConfiguration, keyColumnValueStoreConfiguration, overwrite); checkOptionsWithDiscrepancies(globalWrite, localBasicConfiguration, overwrite); } return keyColumnValueStoreConfiguration.asReadConfiguration(); } } private void setupUpgradeConfiguration(String graphName, ModifiableConfiguration globalWrite) { // If the graph doesn't have a storage version set it and update version if (!globalWrite.has(INITIAL_STORAGE_VERSION)) { globalWrite.set(INITIAL_JANUSGRAPH_VERSION, JanusGraphConstants.VERSION); globalWrite.set(TITAN_COMPATIBLE_VERSIONS, JanusGraphConstants.VERSION); globalWrite.set(INITIAL_STORAGE_VERSION, JanusGraphConstants.STORAGE_VERSION); globalWrite.set(ALLOW_UPGRADE, false); log.info( "graph.storage-version has been upgraded from 1 to {} and graph.janusgraph-version has been upgraded from {} to {} on graph {}", JanusGraphConstants.STORAGE_VERSION, globalWrite.get(INITIAL_JANUSGRAPH_VERSION), JanusGraphConstants.VERSION, graphName); // If the graph has a storage version, but it's lower than the client or server opening the graph upgrade the version and storage version } else if (Integer.parseInt(globalWrite.get(INITIAL_STORAGE_VERSION)) < Integer .parseInt(JanusGraphConstants.STORAGE_VERSION)) { globalWrite.set(INITIAL_JANUSGRAPH_VERSION, JanusGraphConstants.VERSION); globalWrite.set(TITAN_COMPATIBLE_VERSIONS, JanusGraphConstants.VERSION); globalWrite.set(INITIAL_STORAGE_VERSION, JanusGraphConstants.STORAGE_VERSION); globalWrite.set(ALLOW_UPGRADE, false); log.info( "graph.storage-version has been upgraded from {} to {} and graph.janusgraph-version has been upgraded from {} to {} on graph {}", globalWrite.get(INITIAL_STORAGE_VERSION), JanusGraphConstants.STORAGE_VERSION, globalWrite.get(INITIAL_JANUSGRAPH_VERSION), JanusGraphConstants.VERSION, graphName); // If the storage version of the client or server opening the graph is lower than the graph's storage version throw an exception } else if (Integer.parseInt(globalWrite.get(INITIAL_STORAGE_VERSION)) > Integer .parseInt(JanusGraphConstants.STORAGE_VERSION)) { throw new JanusGraphException(String.format(BACKLEVEL_STORAGE_VERSION_EXCEPTION, globalWrite.get(INITIAL_STORAGE_VERSION), JanusGraphConstants.STORAGE_VERSION, graphName)); } else { log.warn( "Warning graph.allow-upgrade is currently set to true on graph {}. Please set graph.allow-upgrade to false in your properties file.", graphName); } } private void setupJanusGraphVersion(ModifiableConfiguration globalWrite) { Preconditions.checkArgument(!globalWrite.has(INITIAL_JANUSGRAPH_VERSION), "Database has already been initialized but not frozen"); globalWrite.set(INITIAL_JANUSGRAPH_VERSION, JanusGraphConstants.VERSION); } private void setupStorageVersion(ModifiableConfiguration globalWrite) { Preconditions.checkArgument(!globalWrite.has(INITIAL_STORAGE_VERSION), "Database has already been initialized but not frozen"); globalWrite.set(INITIAL_STORAGE_VERSION, JanusGraphConstants.STORAGE_VERSION); } private void setupTimestampProvider(ModifiableConfiguration globalWrite, BasicConfiguration localBasicConfiguration, KeyColumnValueStoreManager storeManager) { /* If the configuration does not explicitly set a timestamp provider and * the storage backend both supports timestamps and has a preference for * a specific timestamp provider, then apply the backend's preference. */ if (!localBasicConfiguration.has(TIMESTAMP_PROVIDER)) { StoreFeatures f = storeManager.getFeatures(); final TimestampProviders backendPreference; if (f.hasTimestamps() && null != (backendPreference = f.getPreferredTimestamps())) { globalWrite.set(TIMESTAMP_PROVIDER, backendPreference); log.info("Set timestamps to {} according to storage backend preference", LoggerUtil.sanitizeAndLaunder(globalWrite.get(TIMESTAMP_PROVIDER))); } else { globalWrite.set(TIMESTAMP_PROVIDER, TIMESTAMP_PROVIDER.getDefaultValue()); log.info("Set default timestamp provider {}", LoggerUtil.sanitizeAndLaunder(globalWrite.get(TIMESTAMP_PROVIDER))); } } else { log.info("Using configured timestamp provider {}", localBasicConfiguration.get(TIMESTAMP_PROVIDER)); } } private Map<ConfigElement.PathIdentifier, Object> getGlobalSubset(Map<ConfigElement.PathIdentifier, Object> m) { return Maps.filterEntries(m, entry -> { assert entry.getKey().element.isOption(); return ((ConfigOption) entry.getKey().element).isGlobal(); }); } private Map<ConfigElement.PathIdentifier, Object> getManagedSubset( Map<ConfigElement.PathIdentifier, Object> m) { return Maps.filterEntries(m, entry -> { assert entry.getKey().element.isOption(); return ((ConfigOption) entry.getKey().element).isManaged(); }); } private void checkJanusGraphStorageVersionEquality(ModifiableConfiguration globalWrite, String graphName) { if (!Objects.equals(globalWrite.get(INITIAL_STORAGE_VERSION), JanusGraphConstants.STORAGE_VERSION)) { String storageVersion = (globalWrite.has(INITIAL_STORAGE_VERSION)) ? globalWrite.get(INITIAL_STORAGE_VERSION) : "1"; throw new JanusGraphException(String.format(INCOMPATIBLE_STORAGE_VERSION_EXCEPTION, storageVersion, JanusGraphConstants.STORAGE_VERSION, graphName)); } } private void checkJanusGraphVersion(ModifiableConfiguration globalWrite, BasicConfiguration localBasicConfiguration, KCVSConfiguration keyColumnValueStoreConfiguration, ModifiableConfiguration overwrite) { if (globalWrite.get(INITIAL_JANUSGRAPH_VERSION) == null) { log.info("JanusGraph version has not been initialized"); CompatibilityValidator.validateBackwardCompatibilityWithTitan( globalWrite.get(TITAN_COMPATIBLE_VERSIONS), localBasicConfiguration.get(IDS_STORE_NAME)); setTitanIDStoreNameIfKeystoreNotExists(keyColumnValueStoreConfiguration, overwrite); } } private void setTitanIDStoreNameIfKeystoreNotExists(KCVSConfiguration keyColumnValueStoreConfiguration, ModifiableConfiguration overwrite) { boolean keyStoreExists = keyColumnValueStoreConfiguration.get(IDS_STORE_NAME.getName(), IDS_STORE_NAME.getDatatype()) != null; if (!keyStoreExists) { log.info("Setting {} to {} for Titan compatibility", IDS_STORE_NAME.getName(), JanusGraphConstants.TITAN_ID_STORE_NAME); overwrite.set(IDS_STORE_NAME, JanusGraphConstants.TITAN_ID_STORE_NAME); } } private boolean isUpgradeAllowed(ModifiableConfiguration globalWrite, BasicConfiguration localBasicConfiguration) { if (localBasicConfiguration.has(ALLOW_UPGRADE)) { return localBasicConfiguration.get(ALLOW_UPGRADE); } else if (globalWrite.has(ALLOW_UPGRADE)) { return globalWrite.get(ALLOW_UPGRADE); } return ALLOW_UPGRADE.getDefaultValue(); } private void checkOptionsWithDiscrepancies(ModifiableConfiguration globalWrite, BasicConfiguration localBasicConfiguration, ModifiableConfiguration overwrite) { final boolean managedOverridesAllowed = isManagedOverwritesAllowed(globalWrite, localBasicConfiguration); Set<String> optionsWithDiscrepancies = getOptionsWithDiscrepancies(globalWrite, localBasicConfiguration, overwrite, managedOverridesAllowed); if (optionsWithDiscrepancies.size() > 0 && !managedOverridesAllowed) { final String template = "Local settings present for one or more globally managed options: [%s]. These options are controlled through the %s interface; local settings have no effect."; throw new JanusGraphConfigurationException(String.format(template, Joiner.on(", ").join(optionsWithDiscrepancies), ManagementSystem.class.getSimpleName())); } } private boolean isManagedOverwritesAllowed(ModifiableConfiguration globalWrite, BasicConfiguration localBasicConfiguration) { if (localBasicConfiguration.has(ALLOW_STALE_CONFIG)) { return localBasicConfiguration.get(ALLOW_STALE_CONFIG); } else if (globalWrite.has(ALLOW_STALE_CONFIG)) { return globalWrite.get(ALLOW_STALE_CONFIG); } return ALLOW_STALE_CONFIG.getDefaultValue(); } /** * Check for disagreement between local and backend values for GLOBAL(_OFFLINE) and FIXED options * The point of this check is to find edits to the local config which have no effect (and therefore likely indicate misconfiguration) * * @return Options with discrepancies */ private Set<String> getOptionsWithDiscrepancies(ModifiableConfiguration globalWrite, BasicConfiguration localBasicConfiguration, ModifiableConfiguration overwrite, boolean managedOverridesAllowed) { Set<String> optionsWithDiscrepancies = Sets.newHashSet(); for (Map.Entry<ConfigElement.PathIdentifier, Object> entry : getManagedSubset( localBasicConfiguration.getAll()).entrySet()) { ConfigElement.PathIdentifier pathId = entry.getKey(); assert pathId.element.isOption(); ConfigOption<?> configOption = (ConfigOption<?>) pathId.element; Object localValue = entry.getValue(); // Get the storage backend's setting and compare with localValue Object storeValue = globalWrite.get(configOption, pathId.umbrellaElements); // Check if the value is to be overwritten if (overwrite.has(configOption, pathId.umbrellaElements)) { storeValue = overwrite.get(configOption, pathId.umbrellaElements); } // Most validation predicate implementations disallow null, but we can't assume that here final boolean match = Objects.equals(localValue, storeValue); // Log each option with value disagreement between local and backend configs if (!match) { final String fullOptionName = ConfigElement.getPath(pathId.element, pathId.umbrellaElements); final String template = "Local setting {}={} (Type: {}) is overridden by globally managed value ({}). Use the {} interface instead of the local configuration to control this setting."; Object[] replacements = new Object[] { fullOptionName, localValue, configOption.getType(), storeValue, ManagementSystem.class.getSimpleName() }; if (managedOverridesAllowed) { // Lower log severity when this is enabled log.warn(template, replacements); } else { log.error(template, replacements); } optionsWithDiscrepancies.add(fullOptionName); } } return optionsWithDiscrepancies; } }