This is an automated email from the ASF dual-hosted git repository.
pzampino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new 3d17ede KNOX-2301 and KNOX-2302 (#297)
3d17ede is described below
commit 3d17ede66d29ccf41e95c469e56c696f99262df5
Author: Phil Zampino <[email protected]>
AuthorDate: Mon Mar 23 13:05:13 2020 -0400
KNOX-2301 and KNOX-2302 (#297)
* KNOX-2301 - Trigger discovery for descriptors at gateway start time
* KNOX-2302 - Skip re-deployment of generated topology if the topology has
not changed
---
.../topology/impl/DefaultTopologyService.java | 87 +--
.../apache/knox/gateway/util/TopologyUtils.java | 56 ++
.../topology/DefaultTopologyServiceTest.java | 36 +
.../gateway/services/topology/TopologyService.java | 15 +
.../org/apache/knox/gateway/topology/Provider.java | 30 +
.../org/apache/knox/gateway/topology/Service.java | 41 +-
.../org/apache/knox/gateway/topology/Topology.java | 112 +++-
.../apache/knox/gateway/topology/TopologyTest.java | 742 +++++++++++++++++++++
.../org/apache/knox/gateway/GatewayTestConfig.java | 4 +-
.../org/apache/knox/gateway/GatewayTestDriver.java | 6 +
.../knox/gateway/AmbariServiceDefinitionTest.java | 6 +
.../apache/knox/gateway/GatewayAdminFuncTest.java | 6 +
.../apache/knox/gateway/GatewayAppFuncTest.java | 6 +
.../knox/gateway/GatewayCorrelationIdTest.java | 6 +
.../apache/knox/gateway/GatewayDeployFuncTest.java | 6 +
.../apache/knox/gateway/GatewayHealthFuncTest.java | 6 +
.../knox/gateway/GatewayLocalServiceFuncTest.java | 6 +
.../apache/knox/gateway/GatewayMultiFuncTest.java | 6 +
.../apache/knox/gateway/GatewaySampleFuncTest.java | 6 +
.../apache/knox/gateway/GatewaySslFuncTest.java | 6 +
.../topology/simple/SimpleDescriptorHandler.java | 77 ++-
.../topology/simple/SimpleDescriptorMessages.java | 15 +-
22 files changed, 1183 insertions(+), 98 deletions(-)
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/impl/DefaultTopologyService.java
b/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/impl/DefaultTopologyService.java
index 2c1a62f..66874f9 100644
---
a/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/impl/DefaultTopologyService.java
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/impl/DefaultTopologyService.java
@@ -17,8 +17,6 @@
*/
package org.apache.knox.gateway.services.topology.impl;
-import org.apache.commons.digester3.Digester;
-import org.apache.commons.digester3.binder.DigesterLoader;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
@@ -54,7 +52,6 @@ import org.apache.knox.gateway.topology.TopologyListener;
import org.apache.knox.gateway.topology.TopologyMonitor;
import org.apache.knox.gateway.topology.TopologyProvider;
import org.apache.knox.gateway.topology.Version;
-import org.apache.knox.gateway.topology.builder.TopologyBuilder;
import org.apache.knox.gateway.topology.discovery.ClusterConfigurationMonitor;
import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
import org.apache.knox.gateway.topology.monitor.RemoteConfigurationMonitor;
@@ -62,9 +59,8 @@ import
org.apache.knox.gateway.topology.monitor.RemoteConfigurationMonitorFactor
import org.apache.knox.gateway.topology.simple.SimpleDescriptor;
import org.apache.knox.gateway.topology.simple.SimpleDescriptorFactory;
import org.apache.knox.gateway.topology.validation.TopologyValidator;
-import org.apache.knox.gateway.topology.xml.AmbariFormatXmlTopologyRules;
-import org.apache.knox.gateway.topology.xml.KnoxFormatXmlTopologyRules;
import org.apache.knox.gateway.util.ServiceDefinitionsLoader;
+import org.apache.knox.gateway.util.TopologyUtils;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.xml.sax.SAXException;
@@ -74,6 +70,7 @@ import javax.xml.bind.Marshaller;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@@ -87,8 +84,6 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
-
public class DefaultTopologyService extends FileAlterationListenerAdaptor
implements TopologyService, TopologyMonitor,
TopologyProvider, FileFilter, FileAlterationListener,
ServiceDefinitionChangeListener {
@@ -101,8 +96,6 @@ public class DefaultTopologyService extends
FileAlterationListenerAdaptor implem
public static final List<String> SUPPORTED_TOPOLOGY_FILE_EXTENSIONS =
Collections.unmodifiableList(Arrays.asList("xml", "conf"));
private static GatewayMessages log =
MessagesFactory.get(GatewayMessages.class);
- private static DigesterLoader digesterLoader = newLoader(new
KnoxFormatXmlTopologyRules(),
- new AmbariFormatXmlTopologyRules());
private Map<String, FileAlterationMonitor> monitors = new
ConcurrentHashMap<>();
private File topologiesDirectory;
private File sharedProvidersDirectory;
@@ -153,17 +146,21 @@ public class DefaultTopologyService extends
FileAlterationListenerAdaptor implem
return topology;
}
+ @Override
+ public Topology parse(final InputStream content) throws IOException,
SAXException {
+ return TopologyUtils.parse(content);
+ }
+
private Topology loadTopologyAttempt(File file) throws IOException,
SAXException {
Topology topology;
- Digester digester = digesterLoader.newDigester();
- TopologyBuilder topologyBuilder =
digester.parse(FileUtils.openInputStream(file));
- if (null == topologyBuilder) {
- return null;
- }
- topology = topologyBuilder.build();
- topology.setUri(file.toURI());
- topology.setName(FilenameUtils.removeExtension(file.getName()));
- topology.setTimestamp(file.lastModified());
+ try (InputStream in = FileUtils.openInputStream(file)) {
+ topology = parse(in);
+ if (topology != null) {
+ topology.setUri(file.toURI());
+ topology.setName(FilenameUtils.removeExtension(file.getName()));
+ topology.setTimestamp(file.lastModified());
+ }
+ }
return topology;
}
@@ -556,6 +553,15 @@ public class DefaultTopologyService extends
FileAlterationListenerAdaptor implem
log.remoteConfigurationMonitorStartFailure(remoteMonitor.getClass().getTypeName(),
e.getLocalizedMessage());
}
}
+
+ // Trigger descriptor discovery (KNOX-2301)
+ triggerDescriptorDiscovery();
+ }
+
+ private void triggerDescriptorDiscovery() {
+ for (File descriptor : getDescriptors()) {
+ descriptorsMonitor.onFileChange(descriptor);
+ }
}
@Override
@@ -606,7 +612,6 @@ public class DefaultTopologyService extends
FileAlterationListenerAdaptor implem
@Override
public void start() {
-
}
@Override
@@ -652,50 +657,10 @@ public class DefaultTopologyService extends
FileAlterationListenerAdaptor implem
initListener("shared provider configurations", sharedProvidersDirectory,
spm, spm);
log.configuredMonitoringProviderConfigChangesInDirectory(sharedProvidersDirectory.getAbsolutePath());
- // For all the descriptors currently in the descriptors dir at start-up
time, determine if topology regeneration
- // is required.
- // This happens prior to the start-up loading of the topologies.
- String[] descriptorFilenames = descriptorsDirectory.list();
- if (descriptorFilenames != null) {
- for (String descriptorFilename : descriptorFilenames) {
- if
(DescriptorsMonitor.SUPPORTED_EXTENSIONS.contains(FilenameUtils.getExtension(descriptorFilename)))
{
- String topologyName =
FilenameUtils.getBaseName(descriptorFilename);
- File existingDescriptorFile =
getExistingFile(descriptorsDirectory, topologyName);
- if (existingDescriptorFile != null) {
- // If there isn't a corresponding topology file, or if the
descriptor has been modified since the
- // corresponding topology file was generated, then trigger
generation of one
- File matchingTopologyFile = getExistingFile(topologiesDirectory,
topologyName);
- if (matchingTopologyFile == null ||
matchingTopologyFile.lastModified() < existingDescriptorFile.lastModified()) {
- descriptorsMonitor.onFileChange(existingDescriptorFile);
- } else {
- // If regeneration is NOT required, then we at least need to
report the provider configuration
- // reference relationship (KNOX-1144)
- String normalizedDescriptorPath =
FilenameUtils.normalize(existingDescriptorFile.getAbsolutePath());
-
- // Parse the descriptor to determine the provider config
reference
- SimpleDescriptor sd =
SimpleDescriptorFactory.parse(normalizedDescriptorPath);
- if (sd != null) {
- File referencedProviderConfig =
- getExistingFile(sharedProvidersDirectory,
FilenameUtils.getBaseName(sd.getProviderConfig()));
- if (referencedProviderConfig != null) {
- List<String> references =
-
descriptorsMonitor.getReferencingDescriptors(referencedProviderConfig.getAbsolutePath());
- if (!references.contains(normalizedDescriptorPath)) {
- references.add(normalizedDescriptorPath);
- }
- }
- }
- }
- }
- }
- }
- }
-
// Initialize the remote configuration monitor, if it has been configured
remoteMonitor = RemoteConfigurationMonitorFactory.get(config);
-
- } catch (IOException io) {
- throw new ServiceLifecycleException(io.getMessage(), io);
+ } catch (Exception e) {
+ throw new ServiceLifecycleException(e.getMessage(), e);
}
}
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/util/TopologyUtils.java
b/gateway-server/src/main/java/org/apache/knox/gateway/util/TopologyUtils.java
new file mode 100644
index 0000000..08a3f79
--- /dev/null
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/util/TopologyUtils.java
@@ -0,0 +1,56 @@
+/*
+ * 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.util;
+
+import org.apache.commons.digester3.binder.DigesterLoader;
+import org.apache.knox.gateway.topology.Topology;
+import org.apache.knox.gateway.topology.builder.TopologyBuilder;
+import org.apache.knox.gateway.topology.xml.AmbariFormatXmlTopologyRules;
+import org.apache.knox.gateway.topology.xml.KnoxFormatXmlTopologyRules;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+
+import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
+
+public final class TopologyUtils {
+
+ private static final DigesterLoader digesterLoader = newLoader(new
KnoxFormatXmlTopologyRules(),
+ new
AmbariFormatXmlTopologyRules());
+
+
+ public static Topology parse(final String content) throws IOException,
SAXException {
+ Topology result;
+
+ TopologyBuilder builder = digesterLoader.newDigester().parse(new
StringReader(content));
+ result = builder.build();
+
+ return result;
+ }
+
+ public static Topology parse(final InputStream content) throws IOException,
SAXException {
+ Topology result;
+
+ TopologyBuilder builder = digesterLoader.newDigester().parse(content);
+ result = builder.build();
+
+ return result;
+ }
+
+}
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/topology/DefaultTopologyServiceTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/topology/DefaultTopologyServiceTest.java
index fb9be76..b71ce27 100644
---
a/gateway-server/src/test/java/org/apache/knox/gateway/services/topology/DefaultTopologyServiceTest.java
+++
b/gateway-server/src/test/java/org/apache/knox/gateway/services/topology/DefaultTopologyServiceTest.java
@@ -21,7 +21,10 @@ import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.monitor.FileAlterationListener;
+import org.apache.knox.gateway.GatewayServer;
import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.GatewayServices;
+import org.apache.knox.gateway.services.ServiceType;
import org.apache.knox.gateway.services.topology.impl.DefaultTopologyService;
import org.apache.knox.gateway.services.topology.monitor.DescriptorsMonitor;
import org.apache.knox.gateway.services.security.AliasService;
@@ -38,6 +41,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@@ -177,6 +181,17 @@ public class DefaultTopologyServiceTest {
}
}
+ /**
+ * Set the static GatewayServices field to the specified value.
+ *
+ * @param gws A GatewayServices object, or null.
+ */
+ private void setGatewayServices(final GatewayServices gws) throws Exception {
+ Field gwsField = GatewayServer.class.getDeclaredField("services");
+ gwsField.setAccessible(true);
+ gwsField.set(null, gws);
+ }
+
/*
* KNOX-1014
*
@@ -213,6 +228,12 @@ public class DefaultTopologyServiceTest {
provider.addTopologyChangeListener(topoListener);
provider.reloadTopologies();
+ // GatewayServices mock
+ GatewayServices gws = EasyMock.createNiceMock(GatewayServices.class);
+
EasyMock.expect(gws.getService(ServiceType.TOPOLOGY_SERVICE)).andReturn(provider).anyTimes();
+ EasyMock.replay(gws);
+ setGatewayServices(gws);
+
// Add a simple descriptor to the descriptors dir to verify topology
generation and loading (KNOX-1006)
AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
EasyMock.expect(aliasService.getPasswordFromAliasForGateway(anyObject(String.class))).andReturn(null).anyTimes();
@@ -292,6 +313,7 @@ public class DefaultTopologyServiceTest {
}
} finally {
FileUtils.deleteQuietly(dir);
+ setGatewayServices(null);
}
}
@@ -330,6 +352,12 @@ public class DefaultTopologyServiceTest {
ts.addTopologyChangeListener(topoListener);
ts.reloadTopologies();
+ // GatewayServices mock
+ GatewayServices gws = EasyMock.createNiceMock(GatewayServices.class);
+
EasyMock.expect(gws.getService(ServiceType.TOPOLOGY_SERVICE)).andReturn(ts).anyTimes();
+ EasyMock.replay(gws);
+ setGatewayServices(gws);
+
java.lang.reflect.Field dmField =
ts.getClass().getDeclaredField("descriptorsMonitor");
dmField.setAccessible(true);
DescriptorsMonitor dm = (DescriptorsMonitor) dmField.get(ts);
@@ -389,6 +417,7 @@ public class DefaultTopologyServiceTest {
} finally {
FileUtils.deleteQuietly(dir);
+ setGatewayServices(null);
}
}
@@ -422,6 +451,12 @@ public class DefaultTopologyServiceTest {
ts.addTopologyChangeListener(topoListener);
ts.reloadTopologies();
+ // GatewayServices mock
+ GatewayServices gws = EasyMock.createNiceMock(GatewayServices.class);
+
EasyMock.expect(gws.getService(ServiceType.TOPOLOGY_SERVICE)).andReturn(ts).anyTimes();
+ EasyMock.replay(gws);
+ setGatewayServices(gws);
+
java.lang.reflect.Field dmField =
ts.getClass().getDeclaredField("descriptorsMonitor");
dmField.setAccessible(true);
DescriptorsMonitor dm = (DescriptorsMonitor) dmField.get(ts);
@@ -534,6 +569,7 @@ public class DefaultTopologyServiceTest {
} finally {
FileUtils.deleteQuietly(dir);
+ setGatewayServices(null);
}
}
diff --git
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/topology/TopologyService.java
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/topology/TopologyService.java
index 3266f48..c700014 100644
---
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/topology/TopologyService.java
+++
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/topology/TopologyService.java
@@ -22,8 +22,11 @@ import
org.apache.knox.gateway.service.definition.ServiceDefinitionChangeListene
import org.apache.knox.gateway.services.Service;
import org.apache.knox.gateway.topology.Topology;
import org.apache.knox.gateway.topology.TopologyListener;
+import org.xml.sax.SAXException;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -63,4 +66,16 @@ public interface TopologyService extends Service,
ServiceDefinitionChangeListene
Map<String, List<String>> getServiceTestURLs(Topology t, GatewayConfig
config);
+ /**
+ * Parse the specified XML topology content to produce a Topology object.
+ *
+ * @param content The XML content of the topology.
+ *
+ * @return A Topology object based on the specified content.
+ *
+ * @throws IOException
+ * @throws SAXException
+ */
+ Topology parse(InputStream content) throws IOException, SAXException;
+
}
diff --git
a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Provider.java
b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Provider.java
index 0e20ae8..5d20038 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Provider.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Provider.java
@@ -17,6 +17,9 @@
*/
package org.apache.knox.gateway.topology;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -82,4 +85,31 @@ public class Provider {
this.role = role;
}
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().append(name)
+ .append(role)
+ .append(params)
+ .append(enabled)
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Provider)) {
+ return false;
+ }
+
+ Provider other = (Provider) obj;
+ return (new EqualsBuilder()).append(name, other.name)
+ .append(role, other.role)
+ .append(params, other.params)
+ .append(enabled, other.enabled)
+ .build();
+ }
+
}
diff --git
a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Service.java
b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Service.java
index d65704c..a064039 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Service.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Service.java
@@ -17,6 +17,8 @@
*/
package org.apache.knox.gateway.topology;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.knox.gateway.service.definition.CustomDispatch;
import java.util.ArrayList;
@@ -24,6 +26,7 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
public class Service {
@@ -122,35 +125,22 @@ public class Service {
return false;
}
Service that = (Service) object;
- String thatName = that.getName();
- if (thatName != null && !(thatName.equals(name))) {
- return false;
- }
- String thatRole = that.getRole();
- if (thatRole != null && !thatRole.equals(role)) {
- return false;
- }
- Version thatVersion = that.getVersion();
- if (thatVersion != null && !(thatVersion.equals(version))) {
- return false;
- }
- return true;
+ return (new EqualsBuilder()).append(name, that.name)
+ .append(role, that.role)
+ .append(version, that.version)
+
.append(urls.stream().sorted().collect(Collectors.toList()),
+
that.urls.stream().sorted().collect(Collectors.toList()))
+ .append(params, that.params)
+ .build();
}
@Override
public int hashCode() {
- int hashCode = 17;
- if (getName() != null) {
- hashCode *= 31 * getName().hashCode();
- }
- if (getRole() != null) {
- hashCode *= 31 * getRole().hashCode();
- }
- if (getVersion() != null) {
- hashCode *= 31 * getVersion().hashCode();
- }
-
- return hashCode;
+ return (new HashCodeBuilder(17, 31)).append(name)
+ .append(role)
+ .append(version)
+ .append(urls)
+ .append(params).build();
}
/**
@@ -172,4 +162,5 @@ public class Service {
public void addDispatch(CustomDispatch dispatch) {
this.dispatch = dispatch;
}
+
}
diff --git
a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Topology.java
b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Topology.java
index 8f6e0e2..31cc314 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Topology.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Topology.java
@@ -19,13 +19,17 @@ package org.apache.knox.gateway.topology;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.collections.map.MultiKeyMap;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
public class Topology {
@@ -38,9 +42,13 @@ public class Topology {
private Map<String,Map<String,Provider>> providerMap = new HashMap<>();
public List<Service> services = new ArrayList<>();
private MultiKeyMap serviceMap;
- private List<Application> applications = new ArrayList<>();
+ List<Application> applications = new ArrayList<>();
private Map<String,Application> applicationMap = new HashMap<>();
+ private ProviderComparator providerComparator = new ProviderComparator();
+ private ServiceComparator serviceComparator = new ServiceComparator();
+ private ApplicationComparator appComparator = new ApplicationComparator();
+
public Topology() {
serviceMap = MultiKeyMap.decorate(new HashedMap());
}
@@ -153,4 +161,106 @@ public class Topology {
nameMap.put( provider.getName(), provider );
}
+ @Override
+ public int hashCode() {
+ return (new HashCodeBuilder(17, 31)).append(name)
+
.append(providerList.stream().sorted(providerComparator).collect(Collectors.toList()))
+
.append(services.stream().sorted(serviceComparator).collect(Collectors.toList()))
+
.append(applications.stream().sorted(appComparator).collect(Collectors.toList()))
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ // Has to be a Topology
+ if (!(obj instanceof Topology)) {
+ return false;
+ }
+
+ Topology other = (Topology) obj;
+ if (Objects.equals(this.name,other.name)) {
+ // Order is NOT significant for providers, services, and applications
+
+ if (equalProviders(other)) { // Providers
+ if (equalServices(other)) { // Services
+ if (equalApplications(other)) { // Applications
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private boolean equalProviders(Topology other) {
+ if (providerList != null) {
+ if (other.providerList == null) {
+ return false;
+ }
+
+ List<Provider> mySorted =
providerList.stream().sorted(providerComparator).collect(Collectors.toList());
+ List<Provider> otherSorted =
other.providerList.stream().sorted(providerComparator).collect(Collectors.toList());
+ return mySorted.equals(otherSorted);
+ } else {
+ return (other.providerList == null);
+ }
+ }
+
+ private boolean equalServices(Topology other) {
+ if (services != null) {
+ if (other.services == null) {
+ return false;
+ }
+
+ List<Service> mySorted =
services.stream().sorted(serviceComparator).collect(Collectors.toList());
+ List<Service> otherSorted =
other.services.stream().sorted(serviceComparator).collect(Collectors.toList());
+ return mySorted.equals(otherSorted);
+ } else {
+ return (other.services == null);
+ }
+ }
+
+ private boolean equalApplications(Topology other) {
+ if (applications != null) {
+ if (other.applications == null) {
+ return false;
+ }
+
+ List<Application> mySorted =
applications.stream().sorted(appComparator).collect(Collectors.toList());
+ List<Application> otherSorted =
other.applications.stream().sorted(appComparator).collect(Collectors.toList());
+ return mySorted.equals(otherSorted);
+ } else {
+ return (other.applications == null);
+ }
+ }
+
+ private static final class ProviderComparator implements
Comparator<Provider> {
+ @Override
+ public int compare(Provider o1, Provider o2) {
+ String name1 = o1.getName();
+ return name1 != null ? name1.compareToIgnoreCase(o2.getName()) : 0;
+ }
+ }
+
+ private static final class ServiceComparator implements Comparator<Service> {
+ @Override
+ public int compare(Service o1, Service o2) {
+ String name1 = o1.getName();
+ return name1 != null ? name1.compareToIgnoreCase(o2.getName()) : 0;
+ }
+ }
+
+ private static final class ApplicationComparator implements
Comparator<Application> {
+ @Override
+ public int compare(Application o1, Application o2) {
+ String name1 = o1.getName();
+ return name1 != null ? name1.compareToIgnoreCase(o2.getName()) : 0;
+ }
+ }
+
}
diff --git
a/gateway-spi/src/test/java/org/apache/knox/gateway/topology/TopologyTest.java
b/gateway-spi/src/test/java/org/apache/knox/gateway/topology/TopologyTest.java
new file mode 100644
index 0000000..acacf6a
--- /dev/null
+++
b/gateway-spi/src/test/java/org/apache/knox/gateway/topology/TopologyTest.java
@@ -0,0 +1,742 @@
+/*
+ * 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;
+
+import org.junit.Test;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+public class TopologyTest {
+
+
+ @Test
+ public void testIdenticalTopologyObjectsAreEqual() {
+ Topology t1 = new Topology();
+ Topology t2 = t1;
+ assertEquals(t1, t2);
+ }
+
+ @Test
+ public void testNullProviders() {
+ Topology t1 = new Topology();
+ Topology t2 = new Topology();
+
+ t1.providerList = null;
+ assertNotEquals(t1, t2);
+
+ t1.providerList = Collections.emptyList();
+ t2.providerList = null;
+ assertNotEquals(t1, t2);
+
+ t1.providerList = null;
+ assertEquals(t1, t2);
+ }
+
+ @Test
+ public void testNullServices() {
+ Topology t1 = new Topology();
+ Topology t2 = new Topology();
+
+ t1.services = null;
+ assertNotEquals(t1, t2);
+
+ t1.services = Collections.emptyList();
+ t2.services = null;
+ assertNotEquals(t1, t2);
+
+ t1.services = null;
+ assertEquals(t1, t2);
+ }
+
+ @Test
+ public void testNullApplications() {
+ Topology t1 = new Topology();
+ Topology t2 = new Topology();
+
+ t1.applications = null;
+ assertNotEquals(t1, t2);
+
+ t1.applications = Collections.emptyList();
+ t2.applications = null;
+ assertNotEquals(t1, t2);
+
+ t1.applications = null;
+ assertEquals(t1, t2);
+ }
+
+ @Test
+ public void testEmptyTopologiesWithSameName() {
+ final String name = "tName";
+ Topology t1 = createTopology(name, Collections.emptyList(),
Collections.emptyList(), Collections.emptyList());
+ Topology t2 = createTopology(name, Collections.emptyList(),
Collections.emptyList(), Collections.emptyList());
+ assertEquals(t1, t2);
+ assertEquals("hashcode must be equal if objects are equal.",
t1.hashCode(), t2.hashCode());
+ }
+
+ @Test
+ public void testEmptyTopologiesWithDifferentName() {
+ Topology t1 = createTopology("tName1", Collections.emptyList(),
Collections.emptyList(), Collections.emptyList());
+ Topology t2 = createTopology("tName2", Collections.emptyList(),
Collections.emptyList(), Collections.emptyList());
+ assertNotEquals(t1, t2);
+ assertNotEquals("hashcode must be equal if objects are equal.",
t1.hashCode(), t2.hashCode());
+ }
+
+ @Test
+ public void testTopologiesWithSameServicesInDifferentOrder() {
+ final String name = "topologyX";
+
+ final String serviceName1 = "service1";
+ final String serviceRole1 = "role1";
+ Service service1 = createService(serviceName1, serviceRole1,
Collections.emptyList(), Collections.emptyMap());
+
+ final String serviceName2 = "service2";
+ final String serviceRole2 = "role2";
+ Service service2 = createService(serviceName2, serviceRole2,
Collections.emptyList(), Collections.emptyMap());
+
+ final String serviceName3 = "service3";
+ final String serviceRole3 = "role3";
+ Service service3 = createService(serviceName3, serviceRole3,
Collections.emptyList(), Collections.emptyMap());
+
+ List<Service> services1 = Arrays.asList(service1, service2, service3);
+ List<Service> services2 = Arrays.asList(service2, service3, service1);
+
+ Topology t1 = createTopology(name, Collections.emptyList(), services1,
Collections.emptyList());
+ Topology t2 = createTopology(name, Collections.emptyList(), services2,
Collections.emptyList());
+
+ assertEquals(t1, t2);
+ assertEquals("hashcode must be equal if objects are equal.",
t1.hashCode(), t2.hashCode());
+ }
+
+ @Test
+ public void testTopologiesWithSameServicesWithDifferentURLOrder() {
+ final String name = "topologyX";
+
+ final String url1 = "http://host:1234/path1";
+ final String url2 = "http://host:1234/path2";
+ final String url3 = "http://host:1234/path3";
+
+ final String serviceName1 = "service1";
+ final String serviceRole1 = "role1";
+ Service service1 =
+ createService(serviceName1, serviceRole1,
Arrays.asList(url1, url2, url3), Collections.emptyMap());
+
+ final String serviceName2 = "service2";
+ final String serviceRole2 = "role2";
+ Service service2 =
+ createService(serviceName2, serviceRole2,
Arrays.asList(url2, url3, url1), Collections.emptyMap());
+
+ final String serviceName3 = "service3";
+ final String serviceRole3 = "role3";
+ Service service3 =
+ createService(serviceName3, serviceRole3,
Arrays.asList(url3, url2, url1), Collections.emptyMap());
+
+ List<Service> services1 = Arrays.asList(service1, service2, service3);
+ List<Service> services2 = Arrays.asList(service2, service3, service1);
+
+ Topology t1 = createTopology(name, Collections.emptyList(), services1,
Collections.emptyList());
+ Topology t2 = createTopology(name, Collections.emptyList(), services2,
Collections.emptyList());
+
+ assertEquals(t1, t2);
+ assertEquals("hashcode must be equal if objects are equal.",
t1.hashCode(), t2.hashCode());
+ }
+
+ @Test
+ public void testTopologiesWithSameProvidersInDifferentOrder() {
+ final String name = "topologyX";
+
+ final String name1 = "provider1";
+ final String role1 = "role1";
+ Provider provider1 = createProvider(name1, role1, Collections.emptyMap());
+
+ final String name2 = "provider1";
+ final String role2 = "role1";
+ Provider provider2 = createProvider(name2, role2, Collections.emptyMap());
+
+ final String name3 = "provider1";
+ final String role3 = "role1";
+ Provider provider3 = createProvider(name3, role3, Collections.emptyMap());
+
+ List<Provider> providers1 = Arrays.asList(provider1, provider2, provider3);
+ List<Provider> providers2 = Arrays.asList(provider3, provider2, provider1);
+
+ Topology t1 = createTopology(name, providers1, Collections.emptyList(),
Collections.emptyList());
+ Topology t2 = createTopology(name, providers2, Collections.emptyList(),
Collections.emptyList());
+
+ assertEquals(t1, t2);
+ assertEquals("hashcode must be equal if objects are equal.",
t1.hashCode(), t2.hashCode());
+ }
+
+
+ @Test
+ public void testTopologiesWithSameAppsInDifferentOrder() {
+ final String name = "topologyX";
+
+ final String name1 = "app1";
+ final String role1 = "role1";
+ Application app1 = createApplication(name1, role1,
Collections.emptyList(), Collections.emptyMap());
+
+ final String name2 = "app2";
+ final String role2 = "role2";
+ Application app2 = createApplication(name2, role2,
Collections.emptyList(), Collections.emptyMap());
+
+ final String name3 = "app3";
+ final String role3 = "role3";
+ Application app3 = createApplication(name3, role3,
Collections.emptyList(), Collections.emptyMap());
+
+ List<Application> apps1 = Arrays.asList(app1, app2, app3);
+ List<Application> apps2 = Arrays.asList(app3, app1, app2);
+
+ Topology t1 = createTopology(name, Collections.emptyList(),
Collections.emptyList(), apps1);
+ Topology t2 = createTopology(name, Collections.emptyList(),
Collections.emptyList(), apps2);
+
+ assertEquals(t1, t2);
+ assertEquals("hashcode must be equal if objects are equal.",
t1.hashCode(), t2.hashCode());
+ }
+
+
+ @Test
+ public void testTopologiesAreEqual() {
+ doTestSameTopologies(2, 2, 2);
+ }
+
+ @Test
+ public void testSameTopologiesNoApps() {
+ doTestSameTopologies(2, 2, 0);
+ }
+
+ @Test
+ public void testSameTopologiesNoProviders() {
+ doTestSameTopologies(0, 2, 2);
+ }
+
+ @Test
+ public void testSameTopologiesNoServices() {
+ doTestSameTopologies(2, 0, 2);
+ }
+
+ @Test
+ public void testDifferentAppCount() {
+ final String appName = "aName";
+ final String appRole = "aRole";
+ final String appParamName = "a_key_one";
+ final String appParamValue = "a_value_one";
+ final String appURL = "http://host:1234/app";
+ List<List<Application>> apps = createAppLists(appName, appRole,
appParamName, appParamValue, appURL, 2);
+
+ List<Application> modifiedApps = apps.get(1);
+ modifiedApps.add(createApplication("app2", "app2Role",
Collections.emptyList(), Collections.emptyMap()));
+
+ assertFalse("Expected inequality because there are a different number of
applications.",
+ doTestApplicationEquality(apps.get(0), modifiedApps));
+ }
+
+ @Test
+ public void testDifferentAppNames() {
+ final String appName = "aName";
+ final String appRole = "aRole";
+ final String appURL = "http://host:1234/app";
+ Map<String, String> params = Collections.emptyMap();
+
+ // Since applications' roles get set as the name, vary the role for this
test
+ Application a1 = createApplication(appName, appRole,
Collections.singletonList(appURL), params);
+ Application a2 = createApplication(appName, "differentRole",
Collections.singletonList(appURL), params);
+
+ assertFalse("Expected inequality because there are different application
names.",
+ doTestApplicationEquality(a1, a2));
+ }
+
+ @Test
+ public void testDifferentAppURLCount() {
+ final String appName = "aName";
+ final String appRole = "aRole";
+ final String appURL = "http://host:1234/app";
+ Map<String, String> params = Collections.emptyMap();
+
+ Application a1 = createApplication(appName, appRole,
Collections.singletonList(appURL), params);
+
+ List<String> urls = Arrays.asList(appURL, appURL + "/other");
+ Application a2 = createApplication(appName, appRole, urls, params);
+
+ assertFalse("Expected inequality because there are different number of
application URLs.",
+ doTestApplicationEquality(a1, a2));
+ }
+
+ @Test
+ public void testDifferentAppURLValues() {
+ final String appName = "aName";
+ final String appRole = "aRole";
+ final String appURL = "http://host:1234/app";
+ Map<String, String> params = Collections.emptyMap();
+
+ Application a1 = createApplication(appName, appRole,
Collections.singletonList(appURL), params);
+ Application a2 = createApplication(appName, appRole,
Collections.singletonList(appURL + "/other"), params);
+
+ assertFalse("Expected inequality because there are different application
URL values.",
+ doTestApplicationEquality(a1, a2));
+ }
+
+
+ @Test
+ public void testDifferentAppParamCount() {
+ final String appName = "aName";
+ final String appRole = "aRole";
+ final String appParamName = "a_key_one";
+ final String appParamValue = "a_value_one";
+ final List<String> urls = Collections.emptyList();
+
+ Map<String, String> params = new HashMap<>();
+ params.put(appParamName, appParamValue);
+ Application a1 = createApplication(appName, appRole, urls, params);
+
+ Map<String, String> params2 = new HashMap<>();
+ params.put(appParamName, appParamValue);
+ params.put("anotherName", "anotherValue");
+ Application a2 = createApplication(appName, appRole, urls, params2);
+
+ assertFalse("Expected inequality because there are different number of
application params.",
+ doTestApplicationEquality(a1, a2));
+ }
+
+ @Test
+ public void testDifferentAppParamValues() {
+ final String appName = "aName";
+ final String appRole = "aRole";
+ final String appParamName = "a_key_one";
+ final String appParamValue = "a_value_one";
+ final List<String> urls = Collections.emptyList();
+
+ Map<String, String> params = new HashMap<>();
+ params.put(appParamName, appParamValue);
+ Application a1 = createApplication(appName, appRole, urls, params);
+
+ Map<String, String> params2 = new HashMap<>();
+ params.put(appParamName, "anotherValue");
+ Application a2 = createApplication(appName, appRole, urls, params2);
+
+ assertFalse("Expected inequality because there are different application
param values.",
+ doTestApplicationEquality(a1, a2));
+ }
+
+ @Test
+ public void testDifferentServiceNames() {
+ final String serviceName = "sName";
+ final String serviceRole = "sRole";
+ final String serviceURL = "http://host:1234/service";
+
+ Service s1 = createService(serviceName, serviceRole,
Collections.singletonList(serviceURL), Collections.emptyMap());
+ Service s2 = createService("another", serviceRole,
Collections.singletonList(serviceURL), Collections.emptyMap());
+
+ assertFalse("Expected inequality because there are different service
names.",
+ doTestServiceEquality(s1, s2));
+ }
+
+ @Test
+ public void testDifferentServiceCount() {
+ final String serviceName = "sName";
+ final String serviceRole = "sRole";
+ final String serviceURL = "http://host:1234/service";
+
+ Service s1 = createService(serviceName, serviceRole,
Collections.singletonList(serviceURL), Collections.emptyMap());
+ Service s2 = createService("another", serviceRole,
Collections.singletonList(serviceURL), Collections.emptyMap());
+
+ assertFalse("Expected inequality because there are different number of
services.",
+ doTestServiceEquality(s1, s2));
+ }
+
+
+ @Test
+ public void testDifferentServiceURLCount() {
+ final String serviceName = "sName";
+ final String serviceRole = "sRole";
+ final String serviceURL = "http://host:1234/service";
+
+ Service s1 = createService(serviceName, serviceRole,
Collections.singletonList(serviceURL), Collections.emptyMap());
+ Service s2 =
+ createService(serviceName, serviceRole, Arrays.asList(serviceURL,
serviceURL + "/other"), Collections.emptyMap());
+
+ assertFalse("Expected inequality because there are a different number of
service URLs.",
+ doTestServiceEquality(s1, s2));
+ }
+
+ @Test
+ public void testDifferentServiceURLValues() {
+ final String serviceName = "sName";
+ final String serviceRole = "sRole";
+ final String serviceURL = "http://host:1234/service";
+
+ Service s1 = createService(serviceName, serviceRole,
Collections.singletonList(serviceURL), Collections.emptyMap());
+ Service s2 =
+ createService(serviceName, serviceRole,
Collections.singletonList(serviceURL + "/other"), Collections.emptyMap());
+
+ assertFalse("Expected inequality because there are different service URL
values.",
+ doTestServiceEquality(s1, s2));
+ }
+
+ @Test
+ public void testDifferentServiceParamCount() {
+ final String serviceName = "sName";
+ final String serviceRole = "sRole";
+ final String serviceURL = "http://host:1234/service";
+
+
+ Map<String, String> params = new HashMap<>();
+ params.put("paramOne", "paramOneValue");
+ Service s1 = createService(serviceName, serviceRole,
Collections.singletonList(serviceURL), params);
+
+ Map<String, String> params2 = new HashMap<>();
+ params2.put("paramOne", "paramOneValue");
+ params2.put("paramTwo", "paramTwoValue");
+ Service s2 = createService(serviceName, serviceRole,
Collections.singletonList(serviceURL), params2);
+
+ assertFalse("Expected inequality because there are different number of
service params.",
+ doTestServiceEquality(s1, s2));
+ }
+
+ @Test
+ public void testDifferentServiceParamValues() {
+ final String serviceName = "sName";
+ final String serviceRole = "sRole";
+ final String serviceURL = "http://host:1234/service";
+ final String paramName = "paramOne";
+
+
+ Map<String, String> params = new HashMap<>();
+ params.put(paramName, "paramValue");
+ Service s1 = createService(serviceName, serviceRole,
Collections.singletonList(serviceURL), params);
+
+ Map<String, String> params2 = new HashMap<>();
+ params2.put(paramName, "paramValue" + "DIFFERENT");
+ Service s2 = createService(serviceName, serviceRole,
Collections.singletonList(serviceURL), params2);
+
+ assertFalse("Expected inequality because there are different service param
values.",
+ doTestServiceEquality(s1, s2));
+ }
+
+ @Test
+ public void testDifferentProviderCount() {
+ final String name = "pName";
+ final String role = "pRole";
+
+ Provider p1 = createProvider(name, role, Collections.emptyMap());
+ Provider p2 = createProvider(name, role, Collections.emptyMap());
+
+ assertFalse("Expected inequality because there are a different number of
providers.",
+ doTestProviderEquality(Collections.singletonList(p1),
Arrays.asList(p2, p1)));
+ }
+
+ @Test
+ public void testDifferentProviderNames() {
+ final String role = "pRole";
+
+ Provider p1 = createProvider("p1", role, Collections.emptyMap());
+ Provider p2 = createProvider("p2", role, Collections.emptyMap());
+
+ assertFalse("Expected inequality because there are different provider
names.",
+ doTestProviderEquality(p1, p2));
+ }
+
+ @Test
+ public void testDifferentProviderParamCount() {
+ final String name = "pName";
+ final String role = "pRole";
+
+ Map<String, String> params = new HashMap<>();
+ params.put("paramOne", "p1Value");
+ Provider p1 = createProvider(name, role, params);
+
+ Map<String, String> params2 = new HashMap<>();
+ params2.put("paramOne", "p1Value");
+ params2.put("paramTwo", "p2Value");
+ Provider p2 = createProvider(name, role, params2);
+
+ assertFalse("Expected inequality because there are a different number of
provider params.",
+ doTestProviderEquality(p1, p2));
+ }
+
+ @Test
+ public void testDifferentProviderParamValues() {
+ final String name = "pName";
+ final String role = "pRole";
+ final String paramName = "paramOne";
+
+ Map<String, String> params = new HashMap<>();
+ params.put(paramName, "p1Value");
+ Provider p1 = createProvider(name, role, params);
+
+ Map<String, String> params2 = new HashMap<>();
+ params2.put(paramName, "somethingelse");
+ Provider p2 = createProvider(name, role, params2);
+
+ assertFalse("Expected inequality because there are a different provider
param values.",
+ doTestProviderEquality(p1, p2));
+ }
+
+ private void doTestSameTopologies(int providerCount, int serviceCount, int
appCount) {
+ final String providerName = "pName";
+ final String providerRole = "pRole";
+ final String providerParamName = "p_key_one";
+ final String providerParamValue = "p_value_one";
+ List<List<Provider>> providers =
+ createProviderLists(providerName, providerRole, providerParamName,
providerParamValue, providerCount);
+
+ final String serviceName = "sName";
+ final String serviceRole = "sRole";
+ final String serviceParamName = "s_key_one";
+ final String serviceParamValue = "s_value_one";
+ final String serviceURL = "http://host:1234/service";
+ final List<List<Service>> services =
+ createServiceLists(serviceName, serviceRole, serviceParamName,
serviceParamValue, serviceURL, serviceCount);
+
+ final String appName = "aName";
+ final String appRole = "aRole";
+ final String appParamName = "a_key_one";
+ final String appParamValue = "a_value_one";
+ final String appURL = "http://host:1234/app";
+ List<List<Application>> apps = createAppLists(appName, appRole,
appParamName, appParamValue, appURL, appCount);
+
+ boolean isEqual = doTestEquals(!providers.isEmpty() ? providers.get(0) :
Collections.emptyList(),
+ providers.size() > 1 ? providers.get(1) :
Collections.emptyList(),
+ !services.isEmpty() ? services.get(0) :
Collections.emptyList(),
+ services.size() > 1 ? services.get(1) :
Collections.emptyList(),
+ !apps.isEmpty() ? apps.get(0) :
Collections.emptyList(),
+ apps.size() > 1 ? apps.get(1) :
Collections.emptyList());
+ assertTrue("Expected topologies to be equal.", isEqual);
+ }
+
+ private boolean doTestApplicationEquality(Application app1, Application
app2) {
+ return doTestApplicationEquality(Collections.singletonList(app1),
Collections.singletonList(app2));
+ }
+
+ private boolean doTestApplicationEquality(List<Application> apps1,
List<Application> apps2) {
+ return doTestEquals(Collections.emptyList(),
+ Collections.emptyList(),
+ Collections.emptyList(),
+ Collections.emptyList(),
+ apps1,
+ apps2);
+ }
+
+
+ private boolean doTestServiceEquality(Service s1, Service s2) {
+ return doTestServiceEquality(Collections.singletonList(s1),
Collections.singletonList(s2));
+ }
+
+
+ private boolean doTestServiceEquality(List<Service> svcs1, List<Service>
svcs2) {
+ return doTestEquals(Collections.emptyList(),
+ Collections.emptyList(),
+ svcs1,
+ svcs2,
+ Collections.emptyList(),
+ Collections.emptyList());
+ }
+
+
+ private boolean doTestProviderEquality(Provider p1, Provider p2) {
+ return doTestProviderEquality(Collections.singletonList(p1),
Collections.singletonList(p2));
+ }
+
+ private boolean doTestProviderEquality(List<Provider> p1, List<Provider> p2)
{
+ return doTestEquals(p1,
+ p2,
+ Collections.emptyList(),
+ Collections.emptyList(),
+ Collections.emptyList(),
+ Collections.emptyList());
+ }
+
+
+ /**
+ *
+ * @param provs1
+ * @param provs2
+ * @param svcs1
+ * @param svcs2
+ * @param apps1
+ * @param apps2
+ *
+ * @return true if the topologies are equal; Otherwise, false.
+ */
+ private boolean doTestEquals(final List<Provider> provs1,
+ final List<Provider> provs2,
+ final List<Service> svcs1,
+ final List<Service> svcs2,
+ final List<Application> apps1,
+ final List<Application> apps2) {
+ final String topoName = "testTopology";
+ return doTestEquals(topoName, topoName, provs1, provs2, svcs1, svcs2,
apps1, apps2);
+ }
+
+ /**
+ *
+ * @param name1
+ * @param name2
+ * @param provs1
+ * @param provs2
+ * @param svcs1
+ * @param svcs2
+ * @param apps1
+ * @param apps2
+ *
+ * @return true if the topologies are equal; Otherwise, false.
+ */
+ private boolean doTestEquals(final String name1,
+ final String name2,
+ final List<Provider> provs1,
+ final List<Provider> provs2,
+ final List<Service> svcs1,
+ final List<Service> svcs2,
+ final List<Application> apps1,
+ final List<Application> apps2) {
+
+ Topology t1 = createTopology(name1, provs1, svcs1, apps1);
+ Topology t2 = createTopology(name2, provs2, svcs2, apps2);
+
+ boolean result = t1.equals(t2);
+ if (result) {
+ assertEquals("hashcode must be equal if objects are equal.",
t1.hashCode(), t2.hashCode());
+ }
+ return result;
+ }
+
+ private static Topology createTopology(final String name,
+ final List<Provider> providers,
+ final List<Service> services,
+ final List<Application> applications)
{
+
+ return createTopology(name, providers, services, applications, null);
+ }
+
+ private static Topology createTopology(final String name,
+ final List<Provider> providers,
+ final List<Service> services,
+ final List<Application> applications,
+ final URI uri) {
+
+ Topology t = new Topology();
+ t.providerList = providers;
+ t.services = services;
+ t.applications = applications;
+ t.setName(name);
+ t.setUri(uri);
+ return t;
+ }
+
+ private List<List<Provider>> createProviderLists(final String providerName,
+ final String providerRole,
+ final String
providerParamName,
+ final String
providerParamValue,
+ int count) {
+ List<List<Provider>> result = new ArrayList<>();
+
+ for (int i=0; i < count ; i++) {
+ List<Provider> providers = new ArrayList<>();
+ Map<String, String> providerParams = new HashMap<>();
+ providerParams.put(providerParamName, providerParamValue);
+ providers.add(createProvider(providerName, providerRole,
providerParams));
+ result.add(providers);
+ }
+
+ return result;
+ }
+
+ private List<List<Service>> createServiceLists(final String serviceName,
+ final String serviceRole,
+ final String serviceParamName,
+ final String
serviceParamValue,
+ final String serviceURL,
+ int count) {
+ List<List<Service>> result = new ArrayList<>();
+
+ for (int i=0; i < count ; i++) {
+ List<Service> svcs = new ArrayList<>();
+ Map<String, String> svcParams = new HashMap<>();
+ svcParams.put(serviceParamName, serviceParamValue);
+ svcs.add(createService(serviceName, serviceRole,
Collections.singletonList(serviceURL), svcParams));
+ result.add(svcs);
+ }
+
+ return result;
+ }
+
+ private List<List<Application>> createAppLists(final String appName,
+ final String appRole,
+ final String appParamName,
+ final String appParamValue,
+ final String appURL,
+ int count) {
+ List<List<Application>> result = new ArrayList<>();
+
+ for (int i=0; i < count ; i++) {
+ List<Application> apps = new ArrayList<>();
+ Map<String, String> appParams = new HashMap<>();
+ appParams.put(appParamName, appParamValue);
+ apps.add(createApplication(appName, appRole,
Collections.singletonList(appURL), appParams));
+ result.add(apps);
+ }
+
+ return result;
+ }
+
+ private Service createService(final String name,
+ final String role,
+ final List<String> urls,
+ final Map<String, String> params) {
+ Service s = new Service();
+ s.setName(name);
+ s.setRole(role);
+ s.setUrls(urls);
+ s.setParams(params);
+ return s;
+ }
+
+ private Provider createProvider(final String name,
+ final String role,
+ final Map<String, String> params) {
+ Provider p = new Provider();
+ p.setName(name);
+ p.setRole(role);
+ p.setParams(params);
+
+ return p;
+ }
+
+ private Application createApplication(final String name,
+ final String role,
+ final List<String> urls,
+ final Map<String, String> params) {
+ Application a = new Application();
+ a.setName(name);
+ a.setRole(role);
+ a.setUrls(urls);
+ a.setParams(params);
+
+ return a;
+ }
+
+}
diff --git
a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
index 77206e4..5e46f88 100644
---
a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
+++
b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
@@ -652,12 +652,12 @@ public class GatewayTestConfig extends Configuration
implements GatewayConfig {
@Override
public String getGatewayProvidersConfigDir() {
- return null;
+ return getGatewayConfPath().resolve("shared-providers").toString();
}
@Override
public String getGatewayDescriptorsDir() {
- return null;
+ return getGatewayConfPath().resolve("descriptors").toString();
}
@Override
diff --git
a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestDriver.java
b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestDriver.java
index 7c1cd1f..fb875fb 100644
---
a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestDriver.java
+++
b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestDriver.java
@@ -140,6 +140,12 @@ public class GatewayTestDriver {
File topoDir = new File( config.getGatewayTopologyDir() );
topoDir.mkdirs();
+ File descDir = new File( config.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( config.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File( config.getGatewayDeploymentDir() );
deployDir.mkdirs();
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/AmbariServiceDefinitionTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/AmbariServiceDefinitionTest.java
index 7f64992..d046c50 100644
---
a/gateway-test/src/test/java/org/apache/knox/gateway/AmbariServiceDefinitionTest.java
+++
b/gateway-test/src/test/java/org/apache/knox/gateway/AmbariServiceDefinitionTest.java
@@ -114,6 +114,12 @@ public class AmbariServiceDefinitionTest {
File deployDir = new File( config.getGatewayDeploymentDir() );
deployDir.mkdirs();
+ File descDir = new File( config.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File providerConfigDir = new File( config.getGatewayProvidersConfigDir() );
+ providerConfigDir.mkdirs();
+
setupMockServers();
startGatewayServer();
}
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminFuncTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminFuncTest.java
index 90b673b..48a5406 100644
---
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminFuncTest.java
+++
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminFuncTest.java
@@ -81,6 +81,12 @@ public class GatewayAdminFuncTest {
File topoDir = new File( testConfig.getGatewayTopologyDir() );
topoDir.mkdirs();
+ File descDir = new File( testConfig.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( testConfig.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File( testConfig.getGatewayDeploymentDir() );
deployDir.mkdirs();
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAppFuncTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAppFuncTest.java
index 5791b5d..2178a3b 100644
--- a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAppFuncTest.java
+++ b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAppFuncTest.java
@@ -115,6 +115,12 @@ public class GatewayAppFuncTest {
File topoDir = new File( config.getGatewayTopologyDir() );
topoDir.mkdirs();
+ File descDir = new File( config.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( config.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File( config.getGatewayDeploymentDir() );
deployDir.mkdirs();
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayCorrelationIdTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayCorrelationIdTest.java
index 45a9dde..ad85bee 100644
---
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayCorrelationIdTest.java
+++
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayCorrelationIdTest.java
@@ -97,6 +97,12 @@ public class GatewayCorrelationIdTest {
File topoDir = new File( testConfig.getGatewayTopologyDir() );
topoDir.mkdirs();
+ File descDir = new File( testConfig.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( testConfig.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File( testConfig.getGatewayDeploymentDir() );
deployDir.mkdirs();
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayDeployFuncTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayDeployFuncTest.java
index 339295e..d311b86 100644
---
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayDeployFuncTest.java
+++
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayDeployFuncTest.java
@@ -95,6 +95,12 @@ public class GatewayDeployFuncTest {
File topoDir = new File( testConfig.getGatewayTopologyDir() );
topoDir.mkdirs();
+ File descDir = new File( testConfig.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( testConfig.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File( testConfig.getGatewayDeploymentDir() );
deployDir.mkdirs();
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayHealthFuncTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayHealthFuncTest.java
index 086fe97..552de97 100644
---
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayHealthFuncTest.java
+++
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayHealthFuncTest.java
@@ -104,6 +104,12 @@ public class GatewayHealthFuncTest {
File topoDir = new File(testConfig.getGatewayTopologyDir());
topoDir.mkdirs();
+ File descDir = new File( testConfig.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( testConfig.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File(testConfig.getGatewayDeploymentDir());
deployDir.mkdirs();
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayLocalServiceFuncTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayLocalServiceFuncTest.java
index cfb2235..07ed8f9 100644
---
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayLocalServiceFuncTest.java
+++
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayLocalServiceFuncTest.java
@@ -90,6 +90,12 @@ public class GatewayLocalServiceFuncTest {
File topoDir = new File( testConfig.getGatewayTopologyDir() );
topoDir.mkdirs();
+ File descDir = new File( testConfig.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( testConfig.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File( testConfig.getGatewayDeploymentDir() );
deployDir.mkdirs();
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayMultiFuncTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayMultiFuncTest.java
index f2e9319..c03ec73 100644
---
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayMultiFuncTest.java
+++
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayMultiFuncTest.java
@@ -118,6 +118,12 @@ public class GatewayMultiFuncTest {
File topoDir = new File( config.getGatewayTopologyDir() );
topoDir.mkdirs();
+ File descDir = new File( config.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( config.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File( config.getGatewayDeploymentDir() );
deployDir.mkdirs();
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewaySampleFuncTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewaySampleFuncTest.java
index d36b2c5..29dd829 100644
---
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewaySampleFuncTest.java
+++
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewaySampleFuncTest.java
@@ -81,6 +81,12 @@ public class GatewaySampleFuncTest {
File topoDir = new File( testConfig.getGatewayTopologyDir() );
topoDir.mkdirs();
+ File descDir = new File( testConfig.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( testConfig.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File( testConfig.getGatewayDeploymentDir() );
deployDir.mkdirs();
diff --git
a/gateway-test/src/test/java/org/apache/knox/gateway/GatewaySslFuncTest.java
b/gateway-test/src/test/java/org/apache/knox/gateway/GatewaySslFuncTest.java
index 51506a8..edacab2 100644
--- a/gateway-test/src/test/java/org/apache/knox/gateway/GatewaySslFuncTest.java
+++ b/gateway-test/src/test/java/org/apache/knox/gateway/GatewaySslFuncTest.java
@@ -130,6 +130,12 @@ public class GatewaySslFuncTest {
File topoDir = new File( config.getGatewayTopologyDir() );
topoDir.mkdirs();
+ File descDir = new File( config.getGatewayDescriptorsDir() );
+ descDir.mkdirs();
+
+ File provConfDir = new File( config.getGatewayProvidersConfigDir() );
+ provConfDir.mkdirs();
+
File deployDir = new File( config.getGatewayDeploymentDir() );
deployDir.mkdirs();
diff --git
a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java
b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java
index 8e44005..162b0eb 100644
---
a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java
+++
b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java
@@ -24,13 +24,17 @@ import org.apache.knox.gateway.services.Service;
import org.apache.knox.gateway.services.security.AliasService;
import org.apache.knox.gateway.services.security.KeystoreService;
import org.apache.knox.gateway.services.security.MasterService;
+import org.apache.knox.gateway.services.topology.TopologyService;
+import org.apache.knox.gateway.topology.Topology;
import
org.apache.knox.gateway.topology.discovery.DefaultServiceDiscoveryConfig;
import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryFactory;
import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
@@ -168,11 +172,13 @@ public class SimpleDescriptorHandler {
}
}
+ GatewayServices gws = getGatewayServices(gatewayServices);
+
// Provision the query param encryption password here, rather than
relying on the random password generated
// when the topology is deployed. This is to support Knox HA
deployments, where multiple Knox instances are
// generating topologies based on a shared remote descriptor, and they
must all be able to encrypt/decrypt
// query params with the same credentials. (KNOX-1136)
- if (!provisionQueryParamEncryptionCredential(desc.getName(),
getGatewayServices(gatewayServices))) {
+ if (!provisionQueryParamEncryptionCredential(desc.getName(), gws)) {
log.unableCreatePasswordForEncryption(desc.getName());
}
@@ -185,7 +191,8 @@ public class SimpleDescriptorHandler {
validServiceNames,
serviceVersions,
serviceURLs,
- serviceParams);
+ serviceParams,
+ gws);
}
private static GatewayServices getGatewayServices(Service... services) {
@@ -362,7 +369,8 @@ public class SimpleDescriptorHandler {
final Set<String>
validServiceNames,
final Map<String,
String> serviceVersions,
final Map<String,
List<String>> serviceURLs,
- final Map<String,
Map<String, String>> serviceParams) {
+ final Map<String,
Map<String, String>> serviceParams,
+ final GatewayServices
gwServices) {
Map<String, File> result = new HashMap<>();
File topologyDescriptor = null;
try (StringWriter sw = new StringWriter()) {
@@ -571,13 +579,19 @@ public class SimpleDescriptorHandler {
if (topologyFilename == null) {
topologyFilename = desc.getCluster();
}
+
topologyDescriptor = new File(destDirectory, topologyFilename +
".xml");
- try (OutputStream outputStream =
Files.newOutputStream(topologyDescriptor.toPath());
- OutputStreamWriter outputStreamWriter = new
OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
- BufferedWriter fw = new BufferedWriter(outputStreamWriter)) {
- fw.write(sw.toString());
- fw.flush();
+ if (shouldPersistGeneratedTopology(topologyFilename,
topologyDescriptor, sw.toString(), gwServices)) {
+ log.persistingGeneratedTopology(topologyFilename);
+ try (OutputStream outputStream =
Files.newOutputStream(topologyDescriptor.toPath());
+ OutputStreamWriter outputStreamWriter = new
OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
+ BufferedWriter fw = new
BufferedWriter(outputStreamWriter)) {
+ fw.write(sw.toString());
+ fw.flush();
+ }
+ } else {
+ log.skippingDeploymentOfGeneratedTopology(topologyFilename);
// KNOX-2302
}
} catch (IOException e) {
log.failedToGenerateTopologyFromSimpleDescriptor(topologyDescriptor.getName(),
e);
@@ -589,6 +603,53 @@ public class SimpleDescriptorHandler {
return result;
}
+ /**
+ * Determine whether or not the generated content of the specified
topology should be persisted.
+ *
+ * @param topologyName The name of the topology
+ * @param topologyFile A File reference to the location of the
persisted topology
+ * @param generatedContent The generated topology content
+ * @param gwServices A GatewayServices reference
+ *
+ * @return true, if the generated topology should be persisted; Otherwise,
false.
+ */
+ private static boolean shouldPersistGeneratedTopology(final String
topologyName,
+ final File
topologyFile,
+ final String
generatedContent,
+ final
GatewayServices gwServices) {
+ boolean result = false;
+
+ // Determine if the topology already exists.
+ if (topologyFile.exists()) {
+ // If it does exist, only overwrite it if it has changed
(KNOX-2302)
+ // Compare the generated topology with the in-memory topology
+ Topology existing = null;
+ TopologyService topologyService =
gwServices.getService(ServiceType.TOPOLOGY_SERVICE);
+ for (Topology t : topologyService.getTopologies()) {
+ if (topologyName.equals(t.getName())) {
+ existing = t;
+ break;
+ }
+ }
+
+ if (existing != null) {
+ try (InputStream in = new
ByteArrayInputStream(generatedContent.getBytes(StandardCharsets.UTF_8))) {
+ Topology generatedTopology = topologyService.parse(in);
+ generatedTopology.setName(topologyName);
+ // If the generated toplogy is different from the
existing, then it should be persisted
+ result = !generatedTopology.equals(existing);
+ } catch (Exception e) {
+ log.errorComparingGeneratedTopology(topologyName, e);
+ }
+ } else {
+ result = true; // If the existing Topology could not be
accessed, treat it as a new one
+ }
+ } else {
+ result = true; // It's a new topology, so persist it
+ }
+ return result;
+ }
+
private static Map<String, String> parseHaProviderParam(String paramValue)
{
Map<String, String> result = new HashMap<>();
diff --git
a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorMessages.java
b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorMessages.java
index 08438e6..6beceab 100644
---
a/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorMessages.java
+++
b/gateway-topology-simple/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorMessages.java
@@ -28,7 +28,7 @@ public interface SimpleDescriptorMessages {
text = "Unable to complete service discovery for cluster {0}.")
void failedToDiscoverClusterServices(String descriptorName);
- @Message(level = MessageLevel.ERROR,
+ @Message(level = MessageLevel.WARN,
text = "No valid URLs were discovered for {0} in the {1} cluster.")
void failedToDiscoverClusterServiceURLs(String serviceName, String
clusterName);
@@ -61,4 +61,17 @@ public interface SimpleDescriptorMessages {
text = "Failed to create a password for query string encryption
for {0}." )
void unableCreatePasswordForEncryption(String topologyName);
+ @Message(level = MessageLevel.ERROR,
+ text = "Error comparing the generated {0} topology with the existing
version: {1}" )
+ void errorComparingGeneratedTopology(String topologyName,
+ @StackTrace( level =
MessageLevel.DEBUG) Exception e);
+
+ @Message(level = MessageLevel.INFO,
+ text = "Persisting the generated {0} topology because it either
does not exist or it has changed." )
+ void persistingGeneratedTopology(String topologyName);
+
+ @Message(level = MessageLevel.INFO,
+ text = "Skipping redeployment of the {0} topology because it
already exists and has not changed." )
+ void skippingDeploymentOfGeneratedTopology(String topologyName);
+
}