KNOX-2365 - Knox parses shared provider configuration from Hadoop XML type configuration files (#330)
Main changes in this commit:
- implemented a language that Knox understands when parsing shared provider configuration(s) in Haddop XML type descriptors
- XML/JSON provider configurations are moved out to their own classes from ProviderConfigurationParser
- XML/JSON provider configuration classes implement hashCode/equals properly
- XML/JSON provider classes implement Comparable (based on a predefined provider order) as well as fixing the order of fields and alphabetical order withing parameters when serializing
- changed the separator in Hadoop XML type descriptors from semicolon (;) to hash (#) to conform new requirements
- shared providers that are generated using this framework are read-only on Admimn UI
- end-users can modify/remove any parameters in a provider w/o touching others
diff --git a/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.html b/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.html
index a4e5ea9..b03854a 100644
--- a/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.html
+++ b/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.html
@@ -120,6 +120,7 @@
title="Remove Provider Configuration"
class="btn btn-default btn-sm pull-left"
(click)="deleteConfirmModal.open('md')"
+ *ngIf="showEditOptions()"
data-toggle="tooltip">
<span class="glyphicon glyphicon-trash"></span>
</button>
@@ -129,6 +130,7 @@
class="btn btn-default btn-sm"
[disabled]="!changedProviders"
(click)="discardConfirmModal.open('md')"
+ *ngIf="showEditOptions()"
data-toggle="tooltip">
<span class="glyphicon glyphicon-refresh"></span>
</button>
@@ -138,6 +140,7 @@
class="btn btn-default btn-sm"
[disabled]="!changedProviders"
(click)="persistChanges()"
+ *ngIf="showEditOptions()"
data-toggle="tooltip">
<span class="glyphicon glyphicon-floppy-disk"></span>
</button>
diff --git a/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.ts b/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.ts
index b6ad220..0a0a759 100644
--- a/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.ts
+++ b/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.ts
@@ -48,6 +48,7 @@
resourceContent: string;
providers: Array<ProviderConfig>;
+ readOnlyProviderConfig: boolean;
changedProviders: Array<ProviderConfig>;
descriptor: Descriptor;
@@ -114,11 +115,13 @@
// Parse the JSON representation
contentObj = JSON.parse(this.resourceContent);
this.providers = contentObj['providers'];
+ this.readOnlyProviderConfig = contentObj['readOnly'];
} else if (res.name.endsWith('yaml') || res.name.endsWith('yml')) {
// Parse the YAML representation
let yaml = require('js-yaml');
contentObj = yaml.safeLoad(this.resourceContent);
this.providers = contentObj['providers'];
+ this.readOnlyProviderConfig = contentObj['readOnly'];
} else if (res.name.endsWith('xml')) {
// Parse the XML representation
parseString(this.resourceContent,
@@ -146,6 +149,7 @@
tempProviders.push(providerConfig);
});
this.providers = tempProviders;
+ this.readOnlyProviderConfig = result['gateway'].readOnly;
}
});
}
@@ -563,6 +567,11 @@
if (this.resourceType === 'Descriptors' && this.descriptor.readOnly) {
return !Boolean(this.descriptor.readOnly);
}
+
+ if (this.resourceType === 'Provider Configurations' && this.readOnlyProviderConfig) {
+ return !this.readOnlyProviderConfig;
+ }
+
return true;
}
}
diff --git a/gateway-cm-integration/src/main/java/org/apache/knox/gateway/ClouderaManagerIntegrationMessages.java b/gateway-cm-integration/src/main/java/org/apache/knox/gateway/ClouderaManagerIntegrationMessages.java
index 697e0ea..b5290bd 100644
--- a/gateway-cm-integration/src/main/java/org/apache/knox/gateway/ClouderaManagerIntegrationMessages.java
+++ b/gateway-cm-integration/src/main/java/org/apache/knox/gateway/ClouderaManagerIntegrationMessages.java
@@ -36,11 +36,14 @@
@Message(level = MessageLevel.INFO, text = "Found Knox descriptors {0} in {1}")
void parsedClouderaManagerDescriptor(String descriptorList, String path);
- @Message(level = MessageLevel.INFO, text = "Saved Knox descriptor {0}")
- void savedSimpleDescriptorDescriptor(String path);
+ @Message(level = MessageLevel.INFO, text = "Saved Knox {0} into {1}")
+ void savedResource(String resourceType, String path);
- @Message(level = MessageLevel.INFO, text = "Ignoring {0} Knox descriptor update because it did not change.")
- void descriptorDidNotChange(String descriptorName);
+ @Message(level = MessageLevel.INFO, text = "Ignoring {0} Knox {1} update because it did not change.")
+ void resourceDidNotChange(String resourceName, String resourceType);
+
+ @Message(level = MessageLevel.ERROR, text = "Parsing Knox shared provider configuration {0} failed: {1}")
+ void failedToParseProviderConfiguration(String name, String errorMessage, @StackTrace(level = MessageLevel.DEBUG) Exception e);
@Message(level = MessageLevel.ERROR, text = "Parsing Knox descriptor {0} failed: {1}")
void failedToParseDescriptor(String name, String errorMessage, @StackTrace(level = MessageLevel.DEBUG) Exception e);
diff --git a/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorMonitor.java b/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorMonitor.java
index 15f74af..ab6369b 100644
--- a/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorMonitor.java
+++ b/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorMonitor.java
@@ -48,6 +48,7 @@
private static final String CM_DESCRIPTOR_FILE_EXTENSION = ".cm";
private static final ClouderaManagerIntegrationMessages LOG = MessagesFactory.get(ClouderaManagerIntegrationMessages.class);
+ private final String sharedProvidersDir;
private final String descriptorsDir;
private final long monitoringInterval;
private final ClouderaManagerDescriptorParser cmDescriptorParser;
@@ -55,6 +56,7 @@
public ClouderaManagerDescriptorMonitor(GatewayConfig gatewayConfig, ClouderaManagerDescriptorParser cmDescriptorParser) {
this.cmDescriptorParser = cmDescriptorParser;
+ this.sharedProvidersDir = gatewayConfig.getGatewayProvidersConfigDir();
this.descriptorsDir = gatewayConfig.getGatewayDescriptorsDir();
this.monitoringInterval = gatewayConfig.getClouderaManagerDescriptorsMonitoringInterval();
}
@@ -93,15 +95,38 @@
}
private void processClouderaManagerDescriptor(String descriptorFilePath, String topologyName) {
- cmDescriptorParser.parse(descriptorFilePath, topologyName).forEach(simpleDescriptor -> {
+ final ClouderaManagerDescriptorParserResult result = cmDescriptorParser.parse(descriptorFilePath, topologyName);
+ processSharedProviders(result);
+ processDescriptors(result);
+ }
+
+ private void processSharedProviders(final ClouderaManagerDescriptorParserResult result) {
+ result.getProviders().forEach((key, value) -> {
+ try {
+ final File knoxProviderConfigFile = new File(sharedProvidersDir, key + ".json");
+ final String providersConfiguration = JsonUtils.renderAsJsonString(value);
+ if (isResourceChangedOrNew(knoxProviderConfigFile, providersConfiguration)) {
+ FileUtils.writeStringToFile(knoxProviderConfigFile, providersConfiguration, StandardCharsets.UTF_8);
+ LOG.savedResource("shared provider", knoxProviderConfigFile.getAbsolutePath());
+ } else {
+ LOG.resourceDidNotChange(key, "shared provider");
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ private void processDescriptors(final ClouderaManagerDescriptorParserResult result) {
+ result.getDescriptors().forEach(simpleDescriptor -> {
try {
final File knoxDescriptorFile = new File(descriptorsDir, simpleDescriptor.getName() + ".json");
final String simpleDescriptorJsonString = JsonUtils.renderAsJsonString(simpleDescriptor);
- if (isDescriptorChangedOrNew(knoxDescriptorFile, simpleDescriptorJsonString)) {
+ if (isResourceChangedOrNew(knoxDescriptorFile, simpleDescriptorJsonString)) {
FileUtils.writeStringToFile(knoxDescriptorFile, JsonUtils.renderAsJsonString(simpleDescriptor), StandardCharsets.UTF_8);
- LOG.savedSimpleDescriptorDescriptor(knoxDescriptorFile.getAbsolutePath());
+ LOG.savedResource("descriptor", knoxDescriptorFile.getAbsolutePath());
} else {
- LOG.descriptorDidNotChange(simpleDescriptor.getName());
+ LOG.resourceDidNotChange(simpleDescriptor.getName(), "descriptor");
}
} catch (IOException e) {
LOG.failedToProduceKnoxDescriptor(e.getMessage(), e);
@@ -109,7 +134,7 @@
});
}
- private boolean isDescriptorChangedOrNew(File knoxDescriptorFile, String simpleDescriptorJsonString) throws IOException {
+ private boolean isResourceChangedOrNew(File knoxDescriptorFile, String simpleDescriptorJsonString) throws IOException {
if (knoxDescriptorFile.exists()) {
final String currentContent = FileUtils.readFileToString(knoxDescriptorFile, StandardCharsets.UTF_8);
return !simpleDescriptorJsonString.equals(currentContent);
diff --git a/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParser.java b/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParser.java
index fc8ed8f..122b798 100644
--- a/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParser.java
+++ b/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParser.java
@@ -16,21 +16,31 @@
*/
package org.apache.knox.gateway.cm.descriptor;
+import java.io.File;
import java.nio.file.Paths;
-import java.util.Collections;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Properties;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.knox.gateway.ClouderaManagerIntegrationMessages;
+import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.i18n.messages.MessagesFactory;
import org.apache.knox.gateway.topology.discovery.advanced.AdvancedServiceDiscoveryConfig;
import org.apache.knox.gateway.topology.discovery.advanced.AdvancedServiceDiscoveryConfigChangeListener;
+import org.apache.knox.gateway.topology.simple.JSONProviderConfiguration;
+import org.apache.knox.gateway.topology.simple.JSONProviderConfiguration.JSONProvider;
+import org.apache.knox.gateway.topology.simple.ProviderConfiguration;
+import org.apache.knox.gateway.topology.simple.ProviderConfigurationParser;
import org.apache.knox.gateway.topology.simple.SimpleDescriptor;
import org.apache.knox.gateway.topology.simple.SimpleDescriptorImpl;
import org.apache.knox.gateway.topology.simple.SimpleDescriptorImpl.ApplicationImpl;
@@ -38,6 +48,16 @@
public class ClouderaManagerDescriptorParser implements AdvancedServiceDiscoveryConfigChangeListener {
private static final ClouderaManagerIntegrationMessages log = MessagesFactory.get(ClouderaManagerIntegrationMessages.class);
+
+ //shared provider related constants
+ private static final String CONFIG_NAME_PROVIDER_CONFIGS_PREFIX = "providerConfigs:";
+ private static final String CONFIG_NAME_PROVIDER_CONFIGS_ROLE_PREFIX = "role=";
+ private static final String CONFIG_NAME_PROVIDER_CONFIGS_NAME_PREFIX = "name=";
+ private static final String CONFIG_NAME_PROVIDER_CONFIGS_ENABLED_PREFIX = "enabled=";
+ private static final String CONFIG_NAME_PROVIDER_CONFIGS_PARAM_PREFIX = "param.";
+ private static final String CONFIG_NAME_PROVIDER_CONFIGS_PARAM_REMOVE = "remove";
+
+ //descriptor related constants
private static final String CONFIG_NAME_DISCOVERY_TYPE = "discoveryType";
private static final String CONFIG_NAME_DISCOVERY_ADDRESS = "discoveryAddress";
private static final String CONFIG_NAME_DISCOVERY_USER = "discoveryUser";
@@ -48,10 +68,12 @@
private static final String CONFIG_NAME_SERVICE_URL = "url";
private static final String CONFIG_NAME_SERVICE_VERSION = "version";
- private Map<String, AdvancedServiceDiscoveryConfig> advancedServiceDiscoveryConfigMap;
+ private final Map<String, AdvancedServiceDiscoveryConfig> advancedServiceDiscoveryConfigMap;
+ private final String sharedProvidersDir;
- public ClouderaManagerDescriptorParser() {
- advancedServiceDiscoveryConfigMap = new ConcurrentHashMap<>();
+ public ClouderaManagerDescriptorParser(GatewayConfig gatewayConfig) {
+ this.advancedServiceDiscoveryConfigMap = new ConcurrentHashMap<>();
+ this.sharedProvidersDir = gatewayConfig.getGatewayProvidersConfigDir();
}
/**
@@ -61,7 +83,7 @@
* The path to the configuration file which holds descriptor information in a pre-defined format.
* @return A SimpleDescriptor based on the contents of the given file.
*/
- public Set<SimpleDescriptor> parse(String path) {
+ public ClouderaManagerDescriptorParserResult parse(String path) {
return parse(path, null);
}
@@ -74,33 +96,108 @@
* if set, the parser should only parse a descriptor with the same name
* @return A SimpleDescriptor based on the contents of the given file.
*/
- public Set<SimpleDescriptor> parse(String path, String topologyName) {
+ public ClouderaManagerDescriptorParserResult parse(String path, String topologyName) {
try {
log.parseClouderaManagerDescriptor(path, topologyName == null ? "all topologies" : topologyName);
final Configuration xmlConfiguration = new Configuration(false);
xmlConfiguration.addResource(Paths.get(path).toUri().toURL());
xmlConfiguration.reloadConfiguration();
- final Set<SimpleDescriptor> descriptors = parseXmlConfig(xmlConfiguration, topologyName);
- log.parsedClouderaManagerDescriptor(String.join(", ", descriptors.stream().map(descriptor -> descriptor.getName()).collect(Collectors.toSet())), path);
+ final ClouderaManagerDescriptorParserResult descriptors = parseXmlConfig(xmlConfiguration, topologyName);
+ log.parsedClouderaManagerDescriptor(String.join(", ", descriptors.getDescriptors().stream().map(descriptor -> descriptor.getName()).collect(Collectors.toSet())), path);
return descriptors;
} catch (Exception e) {
log.failedToParseXmlConfiguration(path, e.getMessage(), e);
- return Collections.emptySet();
+ return new ClouderaManagerDescriptorParserResult();
}
}
- private Set<SimpleDescriptor> parseXmlConfig(Configuration xmlConfiguration, String topologyName) {
+ private ClouderaManagerDescriptorParserResult parseXmlConfig(Configuration xmlConfiguration, String topologyName) {
+ final Map<String, ProviderConfiguration> providers = new LinkedHashMap<>();
final Set<SimpleDescriptor> descriptors = new LinkedHashSet<>();
xmlConfiguration.forEach(xmlDescriptor -> {
- String descriptorName = xmlDescriptor.getKey();
- if (topologyName == null || descriptorName.equals(topologyName)) {
- SimpleDescriptor descriptor = parseXmlDescriptor(descriptorName, xmlDescriptor.getValue());
- if (descriptor != null) {
- descriptors.add(descriptor);
+ String xmlConfigurationKey = xmlDescriptor.getKey();
+ if (xmlConfigurationKey.startsWith(CONFIG_NAME_PROVIDER_CONFIGS_PREFIX)) {
+ final String[] providerConfigurations = xmlConfigurationKey.replace(CONFIG_NAME_PROVIDER_CONFIGS_PREFIX, "").split(",");
+ Arrays.asList(providerConfigurations).stream().map(providerConfigurationName -> providerConfigurationName.trim()).forEach(providerConfigurationName -> {
+ final File providerConfigFile = resolveProviderConfiguration(providerConfigurationName);
+ try {
+ final ProviderConfiguration providerConfiguration = getProviderConfiguration(providers, providerConfigFile, providerConfigurationName);
+ providerConfiguration.setReadOnly(true);
+ providerConfiguration.saveOrUpdateProviders(parseProviderConfigurations(xmlDescriptor.getValue(), providerConfiguration));
+ providers.put(providerConfigurationName, providerConfiguration);
+ } catch (Exception e) {
+ log.failedToParseProviderConfiguration(providerConfigurationName, e.getMessage(), e);
+ }
+ });
+ } else {
+ if (topologyName == null || xmlConfigurationKey.equals(topologyName)) {
+ SimpleDescriptor descriptor = parseXmlDescriptor(xmlConfigurationKey, xmlDescriptor.getValue());
+ if (descriptor != null) {
+ descriptors.add(descriptor);
+ }
}
}
});
- return descriptors;
+ return new ClouderaManagerDescriptorParserResult(providers, descriptors);
+ }
+
+ private ProviderConfiguration getProviderConfiguration(Map<String, ProviderConfiguration> providers, File providerConfigFile, String providerConfigName)
+ throws Exception {
+ if (providers.containsKey(providerConfigName)) {
+ return providers.get(providerConfigName);
+ } else {
+ return providerConfigFile == null ? new JSONProviderConfiguration() : ProviderConfigurationParser.parse(providerConfigFile);
+ }
+ }
+
+ private File resolveProviderConfiguration(String providerConfigurationName) {
+ for (String supportedExtension : ProviderConfigurationParser.SUPPORTED_EXTENSIONS) {
+ File providerConfigFile = new File(sharedProvidersDir, providerConfigurationName + "." + supportedExtension);
+ if (providerConfigFile.exists()) {
+ return providerConfigFile;
+ }
+ }
+ return null;
+ }
+
+ private Set<ProviderConfiguration.Provider> parseProviderConfigurations(String xmlValue, ProviderConfiguration providerConfiguration) {
+ final Set<ProviderConfiguration.Provider> providers = new LinkedHashSet<>();
+ final List<String> configurationPairs = Arrays.asList(xmlValue.split("#"));
+ final Set<String> roles = configurationPairs.stream().filter(configurationPair -> configurationPair.trim().startsWith(CONFIG_NAME_PROVIDER_CONFIGS_ROLE_PREFIX))
+ .map(configurationPair -> configurationPair.replace(CONFIG_NAME_PROVIDER_CONFIGS_ROLE_PREFIX, "").trim()).collect(Collectors.toSet());
+ for (String role : roles) {
+ providers.add(parseProvider(configurationPairs, role, providerConfiguration));
+ }
+ return providers;
+ }
+
+ private ProviderConfiguration.Provider parseProvider(List<String> configurationPairs, String role, ProviderConfiguration providerConfiguration) {
+ final JSONProvider provider = new JSONProvider();
+ provider.setRole(role);
+ getParamsForRole(role, providerConfiguration).forEach((key, value) -> provider.addParam(key, value)); //initializing parameters (if any)
+ provider.setEnabled(true); //may be overwritten later, but defaulting to 'true'
+ final Set<String> roleConfigurations = configurationPairs.stream().filter(configurationPair -> configurationPair.trim().startsWith(role))
+ .map(configurationPair -> configurationPair.replace(role + ".", "").trim()).collect(Collectors.toSet());
+ for (String roleConfiguration : roleConfigurations) {
+ if (roleConfiguration.startsWith(CONFIG_NAME_PROVIDER_CONFIGS_PARAM_PREFIX)) {
+ String[] paramKeyValue = roleConfiguration.replace(CONFIG_NAME_PROVIDER_CONFIGS_PARAM_PREFIX, "").split("=", 2);
+ if (CONFIG_NAME_PROVIDER_CONFIGS_PARAM_REMOVE.equals(paramKeyValue[0])) {
+ provider.removeParam(paramKeyValue[1]);
+ } else {
+ provider.addParam(paramKeyValue[0], paramKeyValue[1]);
+ }
+ } else if (roleConfiguration.startsWith(CONFIG_NAME_PROVIDER_CONFIGS_NAME_PREFIX)) {
+ provider.setName(roleConfiguration.replace(CONFIG_NAME_PROVIDER_CONFIGS_NAME_PREFIX, ""));
+ } else if (roleConfiguration.startsWith(CONFIG_NAME_PROVIDER_CONFIGS_ENABLED_PREFIX)) {
+ provider.setEnabled(Boolean.valueOf(roleConfiguration.replace(CONFIG_NAME_PROVIDER_CONFIGS_ENABLED_PREFIX, "")));
+ }
+ }
+ return provider;
+ }
+
+ private Map<String, String> getParamsForRole(String role, ProviderConfiguration providerConfiguration) {
+ final Optional<ProviderConfiguration.Provider> provider = providerConfiguration.getProviders().stream().filter(p -> p.getRole().equals(role)).findFirst();
+ return provider.isPresent() ? provider.get().getParams() : new TreeMap<>();
}
private SimpleDescriptor parseXmlDescriptor(String name, String xmlValue) {
@@ -108,7 +205,7 @@
final SimpleDescriptorImpl descriptor = new SimpleDescriptorImpl();
descriptor.setReadOnly(true);
descriptor.setName(name);
- final String[] configurationPairs = xmlValue.split(";");
+ final String[] configurationPairs = xmlValue.split("#");
for (String configurationPair : configurationPairs) {
String[] parameterPairParts = configurationPair.trim().split("=", 2);
String parameterName = parameterPairParts[0].trim();
diff --git a/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParserResult.java b/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParserResult.java
new file mode 100644
index 0000000..bd24626
--- /dev/null
+++ b/gateway-cm-integration/src/main/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParserResult.java
@@ -0,0 +1,47 @@
+/*
+ * 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.knox.gateway.cm.descriptor;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.knox.gateway.topology.simple.ProviderConfiguration;
+import org.apache.knox.gateway.topology.simple.SimpleDescriptor;
+
+class ClouderaManagerDescriptorParserResult {
+ final Map<String, ProviderConfiguration> providers;
+ final Set<SimpleDescriptor> descriptors;
+
+ ClouderaManagerDescriptorParserResult() {
+ this(Collections.emptyMap(), Collections.emptySet());
+ }
+
+ ClouderaManagerDescriptorParserResult(Map<String, ProviderConfiguration> providers, Set<SimpleDescriptor> descriptors) {
+ this.providers = providers;
+ this.descriptors = descriptors;
+ }
+
+ public Map<String, ProviderConfiguration> getProviders() {
+ return Collections.unmodifiableMap(providers);
+ }
+
+ public Set<SimpleDescriptor> getDescriptors() {
+ return Collections.unmodifiableSet(descriptors);
+ }
+
+}
diff --git a/gateway-cm-integration/src/test/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParserTest.java b/gateway-cm-integration/src/test/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParserTest.java
index f65d36c..d1c4dd6 100644
--- a/gateway-cm-integration/src/test/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParserTest.java
+++ b/gateway-cm-integration/src/test/java/org/apache/knox/gateway/cm/descriptor/ClouderaManagerDescriptorParserTest.java
@@ -20,7 +20,11 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -30,53 +34,71 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.apache.commons.io.FileUtils;
+import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.topology.discovery.advanced.AdvancedServiceDiscoveryConfig;
+import org.apache.knox.gateway.topology.simple.ProviderConfiguration;
import org.apache.knox.gateway.topology.simple.SimpleDescriptor;
import org.apache.knox.gateway.topology.simple.SimpleDescriptor.Application;
import org.apache.knox.gateway.topology.simple.SimpleDescriptor.Service;
+import org.apache.knox.gateway.util.JsonUtils;
+import org.easymock.EasyMock;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
public class ClouderaManagerDescriptorParserTest {
+ @Rule
+ public TemporaryFolder tempDir = new TemporaryFolder();
+
+ private GatewayConfig gatewayConfigMock;
private ClouderaManagerDescriptorParser cmDescriptorParser;
+ private File providersDir;
@Before
- public void setUp() {
- cmDescriptorParser = new ClouderaManagerDescriptorParser();
+ public void setUp() throws IOException {
+ providersDir = tempDir.newFolder("shared-providers");
+ gatewayConfigMock = EasyMock.createNiceMock(GatewayConfig.class);
+ EasyMock.expect(gatewayConfigMock.getGatewayProvidersConfigDir()).andReturn(providersDir.getAbsolutePath()).anyTimes();
+ EasyMock.replay(gatewayConfigMock);
+ cmDescriptorParser = new ClouderaManagerDescriptorParser(gatewayConfigMock);
}
@Test
public void testCMDescriptorParser() throws Exception {
final String testConfigPath = this.getClass().getClassLoader().getResource("testDescriptor.xml").getPath();
- final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath);
+ final ClouderaManagerDescriptorParserResult parserResult = cmDescriptorParser.parse(testConfigPath);
+ final Set<SimpleDescriptor> descriptors = parserResult.getDescriptors();
assertEquals(2, descriptors.size());
final Iterator<SimpleDescriptor> descriptorsIterator = descriptors.iterator();
- validateTopology1(descriptorsIterator.next());
- validateTopology2(descriptorsIterator.next(), true);
+ validateTopology1Descriptors(descriptorsIterator.next());
+ validateTopology2Descriptors(descriptorsIterator.next(), true);
+ validateTestDescriptorProviderConfigs(parserResult.getProviders(), "ldap://localhost:33389");
}
@Test
public void testCMDescriptorParserOnlyTopology2() throws Exception {
final String testConfigPath = this.getClass().getClassLoader().getResource("testDescriptor.xml").getPath();
- final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath, "topology2");
+ final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath, "topology2").getDescriptors();
assertEquals(1, descriptors.size());
- validateTopology2(descriptors.iterator().next(), true);
+ validateTopology2Descriptors(descriptors.iterator().next(), true);
}
@Test
public void testCMDescriptorParserWrongDescriptorContent() throws Exception {
final String testConfigPath = this.getClass().getClassLoader().getResource("testDescriptorConfigurationWithWrongDescriptor.xml").getPath();
- final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath);
+ final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath).getDescriptors();
assertEquals(1, descriptors.size());
final Iterator<SimpleDescriptor> descriptorsIterator = descriptors.iterator();
- validateTopology1(descriptorsIterator.next());
+ validateTopology1Descriptors(descriptorsIterator.next());
}
@Test
public void testCMDescriptorParserWrongXMLContent() throws Exception {
final String testConfigPath = this.getClass().getClassLoader().getResource("testDescriptorConfigurationWithNonHadoopStyleConfiguration.xml").getPath();
- final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath);
+ final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath).getDescriptors();
assertTrue(descriptors.isEmpty());
}
@@ -94,7 +116,7 @@
advancedConfigurationTopology2.put(AdvancedServiceDiscoveryConfig.PARAMETER_NAME_TOPOLOGY_NAME, "topology2");
cmDescriptorParser.onAdvancedServiceDiscoveryConfigurationChange(advancedConfigurationTopology2);
- final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath);
+ final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath).getDescriptors();
assertEquals(2, descriptors.size());
final Iterator<SimpleDescriptor> descriptorsIterator = descriptors.iterator();
SimpleDescriptor topology1 = descriptorsIterator.next();
@@ -105,7 +127,7 @@
SimpleDescriptor topology2 = descriptorsIterator.next();
assertNotNull(topology2);
// topology1 comes with ATLAS and NIFI but the latter one is disabled
- validateTopology2(topology2, false);
+ validateTopology2Descriptors(topology2, false);
}
@Test
@@ -115,7 +137,7 @@
advancedConfiguration.put(buildEnabledParameter("topology1", "oozie"), "true"); //it should not matter if service name is lowercase advanced configuration
advancedConfiguration.put(AdvancedServiceDiscoveryConfig.PARAMETER_NAME_TOPOLOGY_NAME, "topology1");
cmDescriptorParser.onAdvancedServiceDiscoveryConfigurationChange(advancedConfiguration);
- final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath);
+ final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath).getDescriptors();
final Iterator<SimpleDescriptor> descriptorsIterator = descriptors.iterator();
SimpleDescriptor descriptor = descriptorsIterator.next();
assertNotNull(descriptor);
@@ -123,10 +145,54 @@
assertService(descriptor, "OOZIE", null, null, null);
descriptor = descriptorsIterator.next();
- validateTopology2(descriptor, true);
+ validateTopology2Descriptors(descriptor, true);
assertNull(descriptor.getService("OOZIE"));
}
+ @Test
+ public void testCMDescriptorParserModifyingProviderParams() {
+ String testConfigPath = this.getClass().getClassLoader().getResource("testDescriptor.xml").getPath();
+ ClouderaManagerDescriptorParserResult parserResult = cmDescriptorParser.parse(testConfigPath);
+ validateTestDescriptorProviderConfigs(parserResult.getProviders(), "ldap://localhost:33389");
+
+ //saving admin and knoxsso shared-providers with LDAP authentication provider only
+ parserResult.getProviders().forEach((key, value) -> {
+ final File knoxProviderConfigFile = new File(providersDir, key + ".json");
+ final String providersConfiguration = JsonUtils.renderAsJsonString(value);
+ try {
+ FileUtils.writeStringToFile(knoxProviderConfigFile, providersConfiguration, StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ fail("Could not save " + knoxProviderConfigFile.getAbsolutePath());
+ }
+ });
+
+ //updating LDAP URL from ldap://localhost:33389 to ldaps://localhost:33390 in 'admin'
+ testConfigPath = this.getClass().getClassLoader().getResource("testDescriptorWithAdminProviderConfigUpdatedLdapUrl.xml").getPath();
+ parserResult = cmDescriptorParser.parse(testConfigPath);
+ validateTestDescriptorProviderConfigs(parserResult.getProviders(), "ldaps://localhost:33390", true, true);
+ }
+
+ @Test
+ public void testCMDescriptorParserRemovingProviderParams() {
+ String testConfigPath = this.getClass().getClassLoader().getResource("testDescriptor.xml").getPath();
+ ClouderaManagerDescriptorParserResult parserResult = cmDescriptorParser.parse(testConfigPath);
+ //saving admin and knoxsso shared-providers with LDAP authentication provider only
+ parserResult.getProviders().forEach((key, value) -> {
+ final File knoxProviderConfigFile = new File(providersDir, key + ".json");
+ final String providersConfiguration = JsonUtils.renderAsJsonString(value);
+ try {
+ FileUtils.writeStringToFile(knoxProviderConfigFile, providersConfiguration, StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ fail("Could not save " + knoxProviderConfigFile.getAbsolutePath());
+ }
+ });
+
+ //removed 'main.ldapRealm.userDnTemplate' parameter from 'admin'
+ testConfigPath = this.getClass().getClassLoader().getResource("testDescriptorWithAdminProviderConfigRemovedUserDnTemplate.xml").getPath();
+ parserResult = cmDescriptorParser.parse(testConfigPath);
+ validateTestDescriptorProviderConfigs(parserResult.getProviders(), "ldap://localhost:33389", true, false);
+ }
+
private String buildEnabledParameter(String topologyName, String serviceName) {
return AdvancedServiceDiscoveryConfig.PARAMETER_NAME_PREFIX_ENABLED_SERVICE + topologyName + AdvancedServiceDiscoveryConfig.PARAMETER_NAME_POSTFIX_ENABLED_SERVICE + serviceName;
}
@@ -141,7 +207,7 @@
advancedConfiguration.put(AdvancedServiceDiscoveryConfig.PARAMETER_NAME_DISCOVERY_ADDRESS, address);
advancedConfiguration.put(AdvancedServiceDiscoveryConfig.PARAMETER_NAME_DISCOVERY_CLUSTER, cluster);
cmDescriptorParser.onAdvancedServiceDiscoveryConfigurationChange(advancedConfiguration);
- final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath);
+ final Set<SimpleDescriptor> descriptors = cmDescriptorParser.parse(testConfigPath).getDescriptors();
final Iterator<SimpleDescriptor> descriptorsIterator = descriptors.iterator();
SimpleDescriptor descriptor = descriptorsIterator.next();
assertEquals(address, descriptor.getDiscoveryAddress());
@@ -149,7 +215,7 @@
assertEquals("ClouderaManager", descriptor.getDiscoveryType());
}
- private void validateTopology1(SimpleDescriptor descriptor) {
+ private void validateTopology1Descriptors(SimpleDescriptor descriptor) {
assertTrue(descriptor.isReadOnly());
assertEquals("topology1", descriptor.getName());
assertEquals("ClouderaManager", descriptor.getDiscoveryType());
@@ -168,7 +234,7 @@
assertService(descriptor, "HIVE", "1.0", Collections.singletonList("http://localhost:456"), expectedServiceParameters);
}
- private void validateTopology2(SimpleDescriptor descriptor, boolean nifiExpected) {
+ private void validateTopology2Descriptors(SimpleDescriptor descriptor, boolean nifiExpected) {
assertTrue(descriptor.isReadOnly());
assertEquals("topology2", descriptor.getName());
assertEquals("Ambari", descriptor.getDiscoveryType());
@@ -219,4 +285,40 @@
}
}
+ private void validateTestDescriptorProviderConfigs(Map<String, ProviderConfiguration> providers, String expectedLdapUrl) {
+ validateTestDescriptorProviderConfigs(providers, expectedLdapUrl, false, true);
+ }
+
+ private void validateTestDescriptorProviderConfigs(Map<String, ProviderConfiguration> providers, String expectedLdapUrl, boolean onlyAdminIsExpected, boolean expectUserDnTemplateParam) {
+ assertNotNull(providers);
+ assertEquals(onlyAdminIsExpected ? 1 : 2, providers.size());
+ final ProviderConfiguration adminProviderConfig = providers.get("admin");
+ assertTrue(adminProviderConfig.isReadOnly());
+ assertNotNull(adminProviderConfig);
+ assertEquals(1, adminProviderConfig.getProviders().size());
+ final ProviderConfiguration.Provider authenticationProvider = adminProviderConfig.getProviders().iterator().next();
+ assertEquals("authentication", authenticationProvider.getRole());
+ assertEquals("ShiroProvider", authenticationProvider.getName());
+ assertTrue(authenticationProvider.isEnabled());
+ assertEquals(expectUserDnTemplateParam ? 10 : 9, authenticationProvider.getParams().size());
+ assertEquals("30", authenticationProvider.getParams().get("sessionTimeout"));
+ assertEquals("org.apache.knox.gateway.shirorealm.KnoxLdapContextFactory", authenticationProvider.getParams().get("main.ldapContextFactory"));
+ assertEquals("org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm", authenticationProvider.getParams().get("main.ldapRealm"));
+ assertEquals("$ldapContextFactory", authenticationProvider.getParams().get("main.ldapRealm.contextFactory"));
+ assertEquals("simple", authenticationProvider.getParams().get("main.ldapRealm.contextFactory.authenticationMechanism"));
+ assertEquals(expectedLdapUrl, authenticationProvider.getParams().get("main.ldapRealm.contextFactory.url"));
+ assertEquals("uid=guest,ou=people,dc=hadoop,dc=apache,dc=org", authenticationProvider.getParams().get("main.ldapRealm.contextFactory.systemUsername"));
+ assertEquals("${ALIAS=knoxLdapSystemPassword}", authenticationProvider.getParams().get("main.ldapRealm.contextFactory.systemPassword"));
+ if (expectUserDnTemplateParam) {
+ assertEquals("uid={0},ou=people,dc=hadoop,dc=apache,dc=org", authenticationProvider.getParams().get("main.ldapRealm.userDnTemplate"));
+ } else {
+ assertNull(authenticationProvider.getParams().get("main.ldapRealm.userDnTemplate"));
+ }
+ assertEquals("authcBasic", authenticationProvider.getParams().get("urls./**"));
+ if (!onlyAdminIsExpected) {
+ final ProviderConfiguration knoxSsoProviderConfig = providers.get("knoxsso");
+ assertNotNull(knoxSsoProviderConfig);
+ assertEquals(adminProviderConfig, knoxSsoProviderConfig);
+ }
+ }
}
diff --git a/gateway-cm-integration/src/test/resources/testDescriptor.xml b/gateway-cm-integration/src/test/resources/testDescriptor.xml
index a2593df..64308a6 100644
--- a/gateway-cm-integration/src/test/resources/testDescriptor.xml
+++ b/gateway-cm-integration/src/test/resources/testDescriptor.xml
@@ -18,31 +18,48 @@
<property>
<name>topology1</name>
<value>
- discoveryType=ClouderaManager;
- discoveryAddress=http://host:123;
- discoveryUser=user;
- discoveryPasswordAlias=alias;
- cluster=Cluster 1;
- providerConfigRef=topology1-provider;
- app:knoxauth:param1.name=param1.value;
- app:admin-ui;
- HIVE:url=http://localhost:456;
- HIVE:version=1.0;
- HIVE:httpclient.connectionTimeout=5m;
+ discoveryType=ClouderaManager#
+ discoveryAddress=http://host:123#
+ discoveryUser=user#
+ discoveryPasswordAlias=alias#
+ cluster=Cluster 1#
+ providerConfigRef=topology1-provider#
+ app:knoxauth:param1.name=param1.value#
+ app:admin-ui#
+ HIVE:url=http://localhost:456#
+ HIVE:version=1.0#
+ HIVE:httpclient.connectionTimeout=5m#
HIVE:httpclient.socketTimeout=100m
</value>
</property>
<property>
<name>topology2</name>
<value>
- discoveryType=Ambari;
- discoveryAddress=http://host:456;
- cluster=Cluster 2;
- providerConfigRef=topology2-provider;
- ATLAS-API:url=http://localhost:456;
- ATLAS-API:httpclient.connectionTimeout=5m;
- ATLAS-API:httpclient.socketTimeout=100m;
+ discoveryType=Ambari#
+ discoveryAddress=http://host:456#
+ cluster=Cluster 2#
+ providerConfigRef=topology2-provider#
+ ATLAS-API:url=http://localhost:456#
+ ATLAS-API:httpclient.connectionTimeout=5m#
+ ATLAS-API:httpclient.socketTimeout=100m#
NIFI
</value>
</property>
+ <property>
+ <name>providerConfigs:admin, knoxsso</name>
+ <value>
+ role=authentication#
+ authentication.name=ShiroProvider#
+ authentication.param.sessionTimeout=30#
+ authentication.param.main.ldapRealm=org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm#
+ authentication.param.main.ldapContextFactory=org.apache.knox.gateway.shirorealm.KnoxLdapContextFactory#
+ authentication.param.main.ldapRealm.contextFactory=$ldapContextFactory#
+ authentication.param.main.ldapRealm.contextFactory.authenticationMechanism=simple#
+ authentication.param.main.ldapRealm.contextFactory.url=ldap://localhost:33389#
+ authentication.param.main.ldapRealm.contextFactory.systemUsername=uid=guest,ou=people,dc=hadoop,dc=apache,dc=org#
+ authentication.param.main.ldapRealm.contextFactory.systemPassword=${ALIAS=knoxLdapSystemPassword}#
+ authentication.param.main.ldapRealm.userDnTemplate=uid={0},ou=people,dc=hadoop,dc=apache,dc=org#
+ authentication.param.urls./**=authcBasic
+ </value>
+ </property>
</configuration>
\ No newline at end of file
diff --git a/gateway-cm-integration/src/test/resources/testDescriptorConfigurationWithNonHadoopStyleConfiguration.xml b/gateway-cm-integration/src/test/resources/testDescriptorConfigurationWithNonHadoopStyleConfiguration.xml
index 43deb1e..9638739 100644
--- a/gateway-cm-integration/src/test/resources/testDescriptorConfigurationWithNonHadoopStyleConfiguration.xml
+++ b/gateway-cm-integration/src/test/resources/testDescriptorConfigurationWithNonHadoopStyleConfiguration.xml
@@ -18,17 +18,17 @@
<config> <!-- should be property -->
<name>topology1</name>
<value>
- discoveryType=ClouderaManager;
- discoveryAddress=http://host:123;
- discoveryUser=user;
- discoveryPasswordAlias=alias;
- cluster=Cluster 1;
- providerConfigRef=topology1-provider;
- app:knoxauth:param1.name=param1.value;
- app:admin-ui;
- HIVE:url=http://localhost:456;
- HIVE:version=1.0;
- HIVE:httpclient.connectionTimeout=5m;
+ discoveryType=ClouderaManager#
+ discoveryAddress=http://host:123#
+ discoveryUser=user#
+ discoveryPasswordAlias=alias#
+ cluster=Cluster 1#
+ providerConfigRef=topology1-provider#
+ app:knoxauth:param1.name=param1.value#
+ app:admin-ui#
+ HIVE:url=http://localhost:456#
+ HIVE:version=1.0#
+ HIVE:httpclient.connectionTimeout=5m#
HIVE:httpclient.socketTimeout=100m
</value>
</config>
diff --git a/gateway-cm-integration/src/test/resources/testDescriptorConfigurationWithWrongDescriptor.xml b/gateway-cm-integration/src/test/resources/testDescriptorConfigurationWithWrongDescriptor.xml
index a249162..9188843 100644
--- a/gateway-cm-integration/src/test/resources/testDescriptorConfigurationWithWrongDescriptor.xml
+++ b/gateway-cm-integration/src/test/resources/testDescriptorConfigurationWithWrongDescriptor.xml
@@ -18,28 +18,28 @@
<property>
<name>topology1</name>
<value>
- discoveryType=ClouderaManager;
- discoveryAddress=http://host:123;
- discoveryUser=user;
- discoveryPasswordAlias=alias;
- cluster=Cluster 1;
- providerConfigRef=topology1-provider;
- app:knoxauth:param1.name=param1.value;
- app:admin-ui;
- HIVE:url=http://localhost:456;
- HIVE:version=1.0;
- HIVE:httpclient.connectionTimeout=5m;
+ discoveryType=ClouderaManager#
+ discoveryAddress=http://host:123#
+ discoveryUser=user#
+ discoveryPasswordAlias=alias#
+ cluster=Cluster 1#
+ providerConfigRef=topology1-provider#
+ app:knoxauth:param1.name=param1.value#
+ app:admin-ui#
+ HIVE:url=http://localhost:456#
+ HIVE:version=1.0#
+ HIVE:httpclient.connectionTimeout=5m#
HIVE:httpclient.socketTimeout=100m
</value>
</property>
<property>
<name>topology2</name>
<value>
- discoveryType=Ambari;
- discoveryAddress=http://host:456;
- cluster=Cluster 2;
- providerConfigRef=topology2-provider;
- HDFS:url=http://localhost:456;
+ discoveryType=Ambari#
+ discoveryAddress=http://host:456#
+ cluster=Cluster 2#
+ providerConfigRef=topology2-provider#
+ HDFS:url=http://localhost:456#
HDFS:noValueParam <!-- can not be parsed -->
</value>
</property>
diff --git a/gateway-cm-integration/src/test/resources/testDescriptorWithAdminProviderConfigRemovedUserDnTemplate.xml b/gateway-cm-integration/src/test/resources/testDescriptorWithAdminProviderConfigRemovedUserDnTemplate.xml
new file mode 100644
index 0000000..7dcfd3c
--- /dev/null
+++ b/gateway-cm-integration/src/test/resources/testDescriptorWithAdminProviderConfigRemovedUserDnTemplate.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<configuration>
+ <property>
+ <name>providerConfigs:admin</name>
+ <value>
+ role=authentication#
+ authentication.name=ShiroProvider#
+ authentication.param.remove=main.ldapRealm.userDnTemplate
+ </value>
+ </property>
+</configuration>
\ No newline at end of file
diff --git a/gateway-cm-integration/src/test/resources/testDescriptorWithAdminProviderConfigUpdatedLdapUrl.xml b/gateway-cm-integration/src/test/resources/testDescriptorWithAdminProviderConfigUpdatedLdapUrl.xml
new file mode 100644
index 0000000..b22e974
--- /dev/null
+++ b/gateway-cm-integration/src/test/resources/testDescriptorWithAdminProviderConfigUpdatedLdapUrl.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<configuration>
+ <property>
+ <name>providerConfigs:admin</name>
+ <value>
+ role=authentication#
+ authentication.name=ShiroProvider#
+ authentication.param.main.ldapRealm.contextFactory.url=ldaps://localhost:33390
+ </value>
+ </property>
+</configuration>
\ No newline at end of file
diff --git a/gateway-cm-integration/src/test/resources/testDescriptorWithoutDiscoveryDetails.xml b/gateway-cm-integration/src/test/resources/testDescriptorWithoutDiscoveryDetails.xml
index 2773d35..efa6715 100644
--- a/gateway-cm-integration/src/test/resources/testDescriptorWithoutDiscoveryDetails.xml
+++ b/gateway-cm-integration/src/test/resources/testDescriptorWithoutDiscoveryDetails.xml
@@ -18,10 +18,10 @@
<property>
<name>topology1</name>
<value>
- providerConfigRef=topology1-provider;
- app:knoxauth:param1.name=param1.value;
- app:admin-ui;
- HIVE:url=http://localhost:123;
+ providerConfigRef=topology1-provider#
+ app:knoxauth:param1.name=param1.value#
+ app:admin-ui#
+ HIVE:url=http://localhost:123
</value>
</property>
</configuration>
\ No newline at end of file
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
index 7a1c340..3b36ee8 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
@@ -715,7 +715,7 @@
}
private void handleClouderaManagerDescriptors() {
- final ClouderaManagerDescriptorParser cmDescriptorParser = new ClouderaManagerDescriptorParser();
+ final ClouderaManagerDescriptorParser cmDescriptorParser = new ClouderaManagerDescriptorParser(config);
final ClouderaManagerDescriptorMonitor cmDescriptorMonitor = new ClouderaManagerDescriptorMonitor(config, cmDescriptorParser);
final AdvancedServiceDiscoveryConfigurationMonitor advancedServiceDiscoveryConfigurationMonitor = new AdvancedServiceDiscoveryConfigurationMonitor(config);
advancedServiceDiscoveryConfigurationMonitor.registerListener(cmDescriptorParser);
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/ProviderConfigurationParserTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/ProviderConfigurationParserTest.java
index e295302..bde99e8 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/ProviderConfigurationParserTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/ProviderConfigurationParserTest.java
@@ -29,8 +29,8 @@
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
-import java.util.List;
import java.util.Map;
+import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -84,7 +84,7 @@
ProviderConfiguration pc = doTestParseProviderConfiguration(XML, "my-providers.xml");
assertNotNull(pc);
- List<ProviderConfiguration.Provider> providers = pc.getProviders();
+ Set<ProviderConfiguration.Provider> providers = pc.getProviders();
assertNotNull(providers);
assertFalse(providers.isEmpty());
assertEquals(3, providers.size());
@@ -130,13 +130,13 @@
" \"name\":\"ShiroProvider\",\n" +
" \"enabled\":\"true\",\n" +
" \"params\":{\n" +
- " \"sessionTimeout\":\"30\",\n" +
- " \"main.ldapRealm\":\"org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm\",\n" +
" \"main.ldapContextFactory\":\"org.apache.hadoop.gateway.shirorealm.KnoxLdapContextFactory\",\n" +
+ " \"main.ldapRealm\":\"org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm\",\n" +
" \"main.ldapRealm.contextFactory\":\"$ldapContextFactory\",\n" +
" \"main.ldapRealm.userDnTemplate\":\"uid={0},ou=people,dc=hadoop,dc=apache,dc=org\",\n" +
" \"main.ldapRealm.contextFactory.url\":\"ldap://localhost:33389\",\n" +
" \"main.ldapRealm.contextFactory.authenticationMechanism\":\"simple\",\n" +
+ " \"sessionTimeout\":\"30\",\n" +
" \"urls./**\":\"authcBasic\"\n" +
" }\n" +
" },\n" +
@@ -158,8 +158,8 @@
" \"name\":\"HaProvider\",\n" +
" \"enabled\":\"false\",\n" +
" \"params\":{\n" +
- " \"WEBHDFS\":\"maxFailoverAttempts=3;failoverSleep=1000;enabled=true\",\n" +
- " \"HIVE\":\"maxFailoverAttempts=3;failoverSleep=1000;enabled=true\"\n" +
+ " \"HIVE\":\"maxFailoverAttempts=3;failoverSleep=1000;enabled=true\",\n" +
+ " \"WEBHDFS\":\"maxFailoverAttempts=3;failoverSleep=1000;enabled=true\"\n" +
" }\n" +
" }\n" +
" ]\n" +
@@ -168,7 +168,7 @@
ProviderConfiguration pc = doTestParseProviderConfiguration(JSON, "my-providers." + "json");
assertNotNull(pc);
- List<ProviderConfiguration.Provider> providers = pc.getProviders();
+ Set<ProviderConfiguration.Provider> providers = pc.getProviders();
assertNotNull(providers);
assertFalse(providers.isEmpty());
assertEquals(4, providers.size());
@@ -225,7 +225,7 @@
assertNotNull(pc);
- List<ProviderConfiguration.Provider> providers = pc.getProviders();
+ Set<ProviderConfiguration.Provider> providers = pc.getProviders();
assertNotNull(providers);
assertFalse(providers.isEmpty());
assertEquals(4, providers.size());
@@ -235,7 +235,7 @@
}
- private void validateParsedProviders(List<ProviderConfiguration.Provider> providers) throws Exception {
+ private void validateParsedProviders(Set<ProviderConfiguration.Provider> providers) throws Exception {
// Validate the providers
for (ProviderConfiguration.Provider provider : providers) {
String role = provider.getRole();
@@ -255,13 +255,13 @@
assertEquals(params.get("urls./**"), "authcBasic");
// Verify the param order was maintained during parsing (KNOX-1188)
- String[] expectedParameterOrder = new String[] {"sessionTimeout",
+ String[] expectedParameterOrder = new String[] {"main.ldapContextFactory",
"main.ldapRealm",
- "main.ldapContextFactory",
"main.ldapRealm.contextFactory",
- "main.ldapRealm.userDnTemplate",
- "main.ldapRealm.contextFactory.url",
"main.ldapRealm.contextFactory.authenticationMechanism",
+ "main.ldapRealm.contextFactory.url",
+ "main.ldapRealm.userDnTemplate",
+ "sessionTimeout",
"urls./**"};
int index = 0;
for (String name : params.keySet()) {
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
index 58304a2..3610389 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
@@ -49,6 +49,7 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -889,8 +890,8 @@
assertNotNull(generatedProviderConfiguration);
// Compare the generated ProviderConfiguration to the expected one
- List<ProviderConfiguration.Provider> expectedProviders = expected.getProviders();
- List<ProviderConfiguration.Provider> actualProviders = generatedProviderConfiguration.getProviders();
+ Set<ProviderConfiguration.Provider> expectedProviders = expected.getProviders();
+ Set<ProviderConfiguration.Provider> actualProviders = generatedProviderConfiguration.getProviders();
assertEquals("The number of providers should be the same.", expectedProviders.size(), actualProviders.size());
for (ProviderConfiguration.Provider expectedProvider : expectedProviders) {
@@ -905,7 +906,7 @@
* @param expected A Provider that should be among the specified actual providers
* @param actualProviders The set of actual providers.
*/
- private boolean validateProvider(ProviderConfiguration.Provider expected, List<ProviderConfiguration.Provider> actualProviders) {
+ private boolean validateProvider(ProviderConfiguration.Provider expected, Set<ProviderConfiguration.Provider> actualProviders) {
boolean foundMatch = false;
for (ProviderConfiguration.Provider actual : actualProviders) {
diff --git a/gateway-topology-simple/pom.xml b/gateway-topology-simple/pom.xml
index 8d78f87..8e48a2b 100644
--- a/gateway-topology-simple/pom.xml
+++ b/gateway-topology-simple/pom.xml
@@ -63,6 +63,10 @@
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
</dependency>
diff --git a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/JSONProviderConfiguration.java b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/JSONProviderConfiguration.java
new file mode 100644
index 0000000..66199d7
--- /dev/null
+++ b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/JSONProviderConfiguration.java
@@ -0,0 +1,166 @@
+/*
+ * 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.knox.gateway.topology.simple;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+@JsonPropertyOrder({ "providers", "readOnly" })
+public class JSONProviderConfiguration implements ProviderConfiguration {
+
+ @JsonProperty("providers")
+ @JsonSerialize(contentAs = JSONProvider.class)
+ @JsonDeserialize(contentAs = JSONProvider.class)
+ private Set<Provider> providers;
+
+ @JsonProperty("readOnly")
+ private boolean readOnly;
+
+ @Override
+ public Set<Provider> getProviders() {
+ return providers == null ? Collections.emptySet() : Collections.unmodifiableSet(new TreeSet<>(providers));
+ }
+
+ @Override
+ public void saveOrUpdateProviders(Set<Provider> providersToReplace) {
+ if (this.providers == null) {
+ this.providers = new TreeSet<>();
+ this.providers.addAll(providersToReplace);
+ } else {
+ providersToReplace.forEach(providerToAdd -> {
+ final Optional<Provider> toBeRemoved = this.providers.stream().filter(provider -> provider.getRole().equals(providerToAdd.getRole())).findFirst();
+ if (toBeRemoved.isPresent()) {
+ this.providers.remove(toBeRemoved.get());
+ }
+ this.providers.add(providerToAdd);
+ });
+ }
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ @Override
+ public void setReadOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsBuilder.reflectionEquals(this, obj);
+ }
+
+ @JsonPropertyOrder({ "role", "name", "enabled", "params" })
+ public static class JSONProvider implements ProviderConfiguration.Provider, Comparable<JSONProvider> {
+
+ @JsonProperty("role")
+ private String role;
+
+ @JsonProperty("name")
+ private String name;
+
+ @JsonProperty("enabled")
+ private boolean enabled;
+
+ @JsonProperty("params")
+ private Map<String, String> params;
+
+ @Override
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public Map<String, String> getParams() {
+ Map<String, String> result = new TreeMap<>();
+ if (params != null) {
+ result.putAll(params);
+ }
+ return result;
+ }
+
+ public void addParam(String key, String value) {
+ if (params == null) {
+ params = new TreeMap<>();
+ }
+ params.put(key, value);
+ }
+
+ public void removeParam(String key) {
+ if (params != null) {
+ params.remove(key);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsBuilder.reflectionEquals(this, obj);
+ }
+
+ @Override
+ public int compareTo(JSONProvider other) {
+ return Integer.compare(ProviderOrder.getOrdinalForRole(role), ProviderOrder.getOrdinalForRole(other.role));
+ }
+ }
+
+}
diff --git a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderConfiguration.java b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderConfiguration.java
index 4894036..45e3697 100644
--- a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderConfiguration.java
+++ b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderConfiguration.java
@@ -17,13 +17,18 @@
package org.apache.knox.gateway.topology.simple;
-import java.util.List;
import java.util.Map;
+import java.util.Set;
public interface ProviderConfiguration {
- List<Provider> getProviders();
+ Set<Provider> getProviders();
+ void saveOrUpdateProviders(Set<Provider> providers);
+
+ boolean isReadOnly();
+
+ void setReadOnly(boolean readOnly);
interface Provider {
diff --git a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderConfigurationParser.java b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderConfigurationParser.java
index 73b707c..cb8905e 100644
--- a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderConfigurationParser.java
+++ b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderConfigurationParser.java
@@ -16,28 +16,22 @@
*/
package org.apache.knox.gateway.topology.simple;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
-import org.apache.commons.io.FilenameUtils;
-
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.commons.io.FilenameUtils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
public class ProviderConfigurationParser {
private static final JAXBContext jaxbContext = getJAXBContext();
@@ -93,32 +87,26 @@
return EXT_YAML.equals(extension) || EXT_YML.equals(extension);
}
-
static ProviderConfiguration parseXML(File file) throws Exception {
return parseXML(Files.newInputStream(file.toPath()));
}
-
static ProviderConfiguration parseXML(InputStream in) throws Exception {
- XMLProviderConfiguration providerConfig;
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
- providerConfig = (XMLProviderConfiguration) jaxbUnmarshaller.unmarshal(in);
+ XMLProviderConfiguration providerConfig = (XMLProviderConfiguration) jaxbUnmarshaller.unmarshal(in);
return providerConfig;
}
-
static ProviderConfiguration parseJSON(File file) throws IOException {
return parseJSON(Files.newInputStream(file.toPath()));
}
-
static ProviderConfiguration parseJSON(InputStream in) throws IOException {
final ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(in, JSONProviderConfiguration.class);
}
-
static ProviderConfiguration parseYAML(File file) throws IOException {
return parseYAML(Files.newInputStream(file.toPath()));
}
@@ -128,139 +116,4 @@
return mapper.readValue(in, JSONProviderConfiguration.class);
}
-
- // XML Provider Configuration Model
- @XmlRootElement(name="gateway")
- private static class XMLProviderConfiguration implements ProviderConfiguration {
-
- @XmlElement(name="provider")
- private List<XMLProvider> providers;
-
- @Override
- public List<Provider> getProviders() {
- List<Provider> plist = new ArrayList<>();
- if (providers != null) {
- plist.addAll(providers);
- }
- return plist;
- }
-
- @XmlAccessorType(XmlAccessType.NONE)
- private static class XMLProvider implements ProviderConfiguration.Provider {
- @XmlElement
- private String role;
-
- @XmlElement
- private String name;
-
- @XmlElement
- private boolean enabled;
-
- @XmlElement(name = "param")
- private List<XMLParam> params;
-
- @Override
- public String getRole() {
- return role;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public boolean isEnabled() {
- return enabled;
- }
-
- @Override
- public Map<String, String> getParams() {
- Map<String, String> result = new LinkedHashMap<>();
- if (params != null) {
- for (XMLParam p : params) {
- result.put(p.name, p.value);
- }
- }
- return result;
- }
-
- @XmlAccessorType(XmlAccessType.NONE)
- private static class XMLParam {
- @XmlElement
- private String name;
-
- @XmlElement
- private String value;
-
- String getName() {
- return name;
- }
-
- String getValue() {
- return value;
- }
- }
- }
-
- }
-
-
- // JSON/YAML Provider Configuration Model
- private static class JSONProviderConfiguration implements ProviderConfiguration {
-
- @JsonProperty("providers")
- private List<JSONProvider> providers;
-
- @Override
- public List<Provider> getProviders() {
- List<Provider> plist = new ArrayList<>();
- if (providers != null) {
- plist.addAll(providers);
- }
- return plist;
- }
-
- private static class JSONProvider implements ProviderConfiguration.Provider {
-
- @JsonProperty("role")
- private String role;
-
- @JsonProperty("name")
- private String name;
-
- @JsonProperty("enabled")
- private boolean enabled;
-
- @JsonProperty("params")
- private Map<String, String> params;
-
- @Override
- public String getRole() {
- return role;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public boolean isEnabled() {
- return enabled;
- }
-
- @Override
- public Map<String, String> getParams() {
- Map<String, String> result = new LinkedHashMap<>();
- if (params != null) {
- result.putAll(params);
- }
- return result;
- }
- }
-
- }
-
-
}
diff --git a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderOrder.java b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderOrder.java
new file mode 100644
index 0000000..73fbeb8
--- /dev/null
+++ b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/ProviderOrder.java
@@ -0,0 +1,40 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.knox.gateway.topology.simple;
+
+public enum ProviderOrder {
+ WEBAPPSEC(1), AUTHENTICATION(2), FEDERATION(3), IDENTITY_ASSERTION(4), AUTHORIZATION(5), HOSTMAP(6), HA(7);
+
+ final int ordinal;
+ final String name;
+
+ ProviderOrder(int ordinal) {
+ this.ordinal = ordinal;
+ this.name = name().replace("_", "-");
+ }
+
+ static int getOrdinalForRole(String role) {
+ int count = 0;
+ for (ProviderOrder providerOrder : values()) {
+ count++;
+ if (providerOrder.name.equalsIgnoreCase(role)) {
+ return providerOrder.ordinal;
+ }
+ }
+ return ++count;
+ }
+}
diff --git a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/XMLProviderConfiguration.java b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/XMLProviderConfiguration.java
new file mode 100644
index 0000000..6606206
--- /dev/null
+++ b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/XMLProviderConfiguration.java
@@ -0,0 +1,152 @@
+/*
+ * 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.knox.gateway.topology.simple;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+@XmlRootElement(name = "gateway")
+@XmlAccessorType(XmlAccessType.FIELD)
+class XMLProviderConfiguration implements ProviderConfiguration {
+
+ @XmlElement(name = "provider", type=XMLProvider.class)
+ private Set<Provider> providers;
+
+ @XmlElement(name = "readOnly")
+ private boolean readOnly;
+
+ @Override
+ public Set<Provider> getProviders() {
+ return Collections.unmodifiableSet(providers);
+ }
+
+ @Override
+ public void saveOrUpdateProviders(Set<Provider> providersToReplace) {
+ if (providers == null) {
+ providers = new TreeSet<>();
+ this.providers.addAll(providersToReplace);
+ } else {
+ providersToReplace.forEach(providerToAdd -> {
+ final Optional<Provider> toBeRemoved = providers.stream().filter(provider -> provider.getRole().equals(providerToAdd.getRole())).findFirst();
+ if (toBeRemoved.isPresent()) {
+ providers.remove(toBeRemoved.get());
+ }
+ providers.add(providerToAdd);
+ });
+ }
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ @Override
+ public void setReadOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsBuilder.reflectionEquals(this, obj);
+ }
+
+ @XmlAccessorType(XmlAccessType.NONE)
+ @XmlType(propOrder = { "role", "name", "enabled", "params" })
+ private static class XMLProvider implements ProviderConfiguration.Provider, Comparable<XMLProvider> {
+ @XmlElement
+ private String role;
+
+ @XmlElement
+ private String name;
+
+ @XmlElement
+ private boolean enabled;
+
+ @XmlElement(name = "param")
+ private List<XMLParam> params;
+
+ @Override
+ public String getRole() {
+ return role;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public Map<String, String> getParams() {
+ Map<String, String> result = new TreeMap<>();
+ if (params != null) {
+ for (XMLParam p : params) {
+ result.put(p.name, p.value);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsBuilder.reflectionEquals(this, obj);
+ }
+
+ @XmlAccessorType(XmlAccessType.NONE)
+ private static class XMLParam {
+ @XmlElement
+ private String name;
+
+ @XmlElement
+ private String value;
+ }
+
+ @Override
+ public int compareTo(XMLProvider other) {
+ return Integer.compare(ProviderOrder.getOrdinalForRole(role), ProviderOrder.getOrdinalForRole(other.role));
+ }
+ }
+}