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);
+
 }

Reply via email to