Repository: knox Updated Branches: refs/heads/master 5b57a5151 -> cd3367599
http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java index f10f97b..b53d3b8 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java @@ -565,6 +565,9 @@ public interface GatewayMessages { @Message( level = MessageLevel.INFO, text = "Remote configuration monitor deleted {0} configuration file {1} based on remote change." ) void deletedRemoteConfigFile(final String type, final String configFileName); + @Message( level = MessageLevel.ERROR, text = "Failed to delete remote {0} file {1}." ) + void failedToDeletedRemoteConfigFile(final String type, final String configFileName); + @Message( level = MessageLevel.ERROR, text = "An error occurred while processing {0} : {1}" ) void simpleDescriptorHandlingError(final String simpleDesc, @StackTrace(level = MessageLevel.DEBUG) Exception e); http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-server/src/main/java/org/apache/knox/gateway/services/topology/impl/DefaultTopologyService.java ---------------------------------------------------------------------- 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 5985d5f..011975a 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 @@ -40,6 +40,7 @@ import org.apache.knox.gateway.i18n.messages.MessagesFactory; import org.apache.knox.gateway.service.definition.ServiceDefinition; import org.apache.knox.gateway.services.GatewayServices; import org.apache.knox.gateway.services.ServiceLifecycleException; +import org.apache.knox.gateway.services.config.client.RemoteConfigurationRegistryClient; import org.apache.knox.gateway.services.security.AliasService; import org.apache.knox.gateway.services.topology.TopologyService; import org.apache.knox.gateway.topology.ClusterConfigurationMonitorService; @@ -426,7 +427,22 @@ public class DefaultTopologyService @Override public boolean deployProviderConfiguration(String name, String content) { - return writeConfig(sharedProvidersDirectory, name, content); + boolean result; + + // Whether the remote configuration registry is being employed or not, write the file locally + result = writeConfig(sharedProvidersDirectory, name, content); + + // If the remote configuration registry is being employed, persist it there also + if (remoteMonitor != null) { + RemoteConfigurationRegistryClient client = remoteMonitor.getClient(); + if (client != null) { + String entryPath = "/knox/config/shared-providers/" + name; + client.createEntry(entryPath, content); + result = (client.getEntryData(entryPath) != null); + } + } + + return result; } @Override @@ -444,16 +460,29 @@ public class DefaultTopologyService public boolean deleteProviderConfiguration(String name) { boolean result = false; + // Determine if the file exists, and if so, if there are any descriptors referencing it + boolean hasReferences = false; File providerConfig = getExistingFile(sharedProvidersDirectory, name); if (providerConfig != null) { List<String> references = descriptorsMonitor.getReferencingDescriptors(providerConfig.getAbsolutePath()); - if (references.isEmpty()) { + hasReferences = !references.isEmpty(); + } else { + result = true; // If it already does NOT exist, then the delete effectively succeeded + } + + // If the local file does not exist, or it does exist and there are NOT any referencing descriptors + if (providerConfig == null || !hasReferences) { + + // If the remote config monitor is configured, attempt to delete the provider configuration from the remote + // registry, even if it does not exist locally. + deleteRemoteEntry("/knox/config/shared-providers", name); + + if (providerConfig != null) { + // Whether the remote configuration registry is being employed or not, delete the local file if it exists result = providerConfig.delete(); - } else { - log.preventedDeletionOfSharedProviderConfiguration(providerConfig.getAbsolutePath()); } } else { - result = true; // If it already does NOT exist, then the delete effectively succeeded + log.preventedDeletionOfSharedProviderConfiguration(providerConfig.getAbsolutePath()); } return result; @@ -461,7 +490,22 @@ public class DefaultTopologyService @Override public boolean deployDescriptor(String name, String content) { - return writeConfig(descriptorsDirectory, name, content); + boolean result; + + // Whether the remote configuration registry is being employed or not, write the file locally + result = writeConfig(descriptorsDirectory, name, content); + + // If the remote configuration registry is being employed, persist it there also + if (remoteMonitor != null) { + RemoteConfigurationRegistryClient client = remoteMonitor.getClient(); + if (client != null) { + String entryPath = "/knox/config/descriptors/" + name; + client.createEntry(entryPath, content); + result = (client.getEntryData(entryPath) != null); + } + } + + return result; } @Override @@ -477,8 +521,16 @@ public class DefaultTopologyService @Override public boolean deleteDescriptor(String name) { + boolean result; + + // If the remote config monitor is configured, delete the descriptor from the remote registry + deleteRemoteEntry("/knox/config/descriptors", name); + + // Whether the remote configuration registry is being employed or not, delete the local file File descriptor = getExistingFile(descriptorsDirectory, name); - return (descriptor == null) || descriptor.delete(); + result = (descriptor == null) || descriptor.delete(); + + return result; } @Override @@ -642,6 +694,38 @@ public class DefaultTopologyService } /** + * Delete the entry in the remote configuration registry, which matches the specified resource name. + * + * @param entryParent The remote registry path in which the entry exists. + * @param name The name of the entry (typically without any file extension). + * + * @return true, if the entry is deleted, or did not exist; otherwise, false. + */ + private boolean deleteRemoteEntry(String entryParent, String name) { + boolean result = true; + + if (remoteMonitor != null) { + RemoteConfigurationRegistryClient client = remoteMonitor.getClient(); + if (client != null) { + List<String> existingProviderConfigs = client.listChildEntries(entryParent); + for (String entryName : existingProviderConfigs) { + if (FilenameUtils.getBaseName(entryName).equals(name)) { + String entryPath = entryParent + "/" + entryName; + client.deleteEntry(entryPath); + result = !client.entryExists(entryPath); + if (!result) { + log.failedToDeletedRemoteConfigFile("descriptor", name); + } + break; + } + } + } + } + + return result; + } + + /** * Utility method for listing the files in the specified directory. * This method is "nicer" than the File#listFiles() because it will not return null. * http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-server/src/main/java/org/apache/knox/gateway/topology/monitor/DefaultRemoteConfigurationMonitor.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/topology/monitor/DefaultRemoteConfigurationMonitor.java b/gateway-server/src/main/java/org/apache/knox/gateway/topology/monitor/DefaultRemoteConfigurationMonitor.java index 37d1ca6..6db3952 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/topology/monitor/DefaultRemoteConfigurationMonitor.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/topology/monitor/DefaultRemoteConfigurationMonitor.java @@ -97,6 +97,11 @@ class DefaultRemoteConfigurationMonitor implements RemoteConfigurationMonitor { } @Override + public RemoteConfigurationRegistryClient getClient() { + return client; + } + + @Override public void start() throws Exception { if (client == null) { throw new IllegalStateException("Failed to acquire a remote configuration registry client."); http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java ---------------------------------------------------------------------- diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java b/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java index a987433..6524ca0 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java @@ -1924,8 +1924,10 @@ public class KnoxCLI extends Configured implements Tool { if (client != null) { out.println("Provider Configurations (@" + client.getAddress() + ")"); List<String> entries = client.listChildEntries(PROVIDER_CONFIG_ENTRY); - for (String entry : entries) { - out.println(entry); + if (entries != null) { + for (String entry : entries) { + out.println(entry); + } } out.println(); } @@ -1948,8 +1950,10 @@ public class KnoxCLI extends Configured implements Tool { if (client != null) { out.println("Descriptors (@" + client.getAddress() + ")"); List<String> entries = client.listChildEntries(DESCRIPTORS_ENTRY); - for (String entry : entries) { - out.println(entry); + if (entries != null) { + for (String entry : entries) { + out.println(entry); + } } out.println(); } http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/TopologiesResource.java ---------------------------------------------------------------------- diff --git a/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/TopologiesResource.java b/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/TopologiesResource.java index 823f9ad..3a39ce2 100644 --- a/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/TopologiesResource.java +++ b/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/TopologiesResource.java @@ -37,6 +37,8 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -50,7 +52,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.MediaType.APPLICATION_XML; @@ -67,6 +71,7 @@ public class TopologiesResource { private static final String XML_EXT = ".xml"; private static final String JSON_EXT = ".json"; + private static final String YAML_EXT = ".yml"; private static final String TOPOLOGIES_API_PATH = "topologies"; private static final String SINGLE_TOPOLOGY_API_PATH = TOPOLOGIES_API_PATH + "/{id}"; @@ -77,6 +82,13 @@ public class TopologiesResource { private static GatewaySpiMessages log = MessagesFactory.get(GatewaySpiMessages.class); + private static final Map<MediaType, String> mediaTypeFileExtensions = new HashMap<>(); + static { + mediaTypeFileExtensions.put(MediaType.APPLICATION_XML_TYPE, XML_EXT); + mediaTypeFileExtensions.put(MediaType.APPLICATION_JSON_TYPE, JSON_EXT); + mediaTypeFileExtensions.put(MediaType.TEXT_PLAIN_TYPE, YAML_EXT); + } + @Context private HttpServletRequest request; @@ -288,9 +300,9 @@ public class TopologiesResource { @PUT - @Consumes({APPLICATION_XML}) + @Consumes({APPLICATION_XML, APPLICATION_JSON, TEXT_PLAIN}) @Path(SINGLE_PROVIDERCONFIG_API_PATH) - public Response uploadProviderConfiguration(@PathParam("name") String name, String content) { + public Response uploadProviderConfiguration(@PathParam("name") String name, @Context HttpHeaders headers, String content) { Response response = null; GatewayServices gs = @@ -298,14 +310,18 @@ public class TopologiesResource { TopologyService ts = gs.getService(GatewayServices.TOPOLOGY_SERVICE); - boolean isUpdate = configFileExists(ts.getProviderConfigurations(), name); + File existing = getExistingConfigFile(ts.getProviderConfigurations(), name); + boolean isUpdate = (existing != null); + + // If it's an update, then use the matching existing filename; otherwise, use the media type to determine the file + // extension. + String filename = isUpdate ? existing.getName() : getFileNameForResource(name, headers); - String filename = name.endsWith(XML_EXT) ? name : name + XML_EXT; if (ts.deployProviderConfiguration(filename, content)) { try { if (isUpdate) { response = Response.noContent().build(); - } else{ + } else { response = created(new URI(buildHref(request))).build(); } } catch (URISyntaxException e) { @@ -317,23 +333,10 @@ public class TopologiesResource { return response; } - - private boolean configFileExists(Collection<File> existing, String candidateName) { - boolean result = false; - for (File exists : existing) { - if (FilenameUtils.getBaseName(exists.getName()).equals(candidateName)) { - result = true; - break; - } - } - return result; - } - - @PUT - @Consumes({APPLICATION_JSON}) + @Consumes({APPLICATION_JSON, TEXT_PLAIN}) @Path(SINGLE_DESCRIPTOR_API_PATH) - public Response uploadSimpleDescriptor(@PathParam("name") String name, String content) { + public Response uploadSimpleDescriptor(@PathParam("name") String name, @Context HttpHeaders headers, String content) { Response response = null; GatewayServices gs = @@ -341,9 +344,13 @@ public class TopologiesResource { TopologyService ts = gs.getService(GatewayServices.TOPOLOGY_SERVICE); - boolean isUpdate = configFileExists(ts.getDescriptors(), name); + File existing = getExistingConfigFile(ts.getDescriptors(), name); + boolean isUpdate = (existing != null); + + // If it's an update, then use the matching existing filename; otherwise, use the media type to determine the file + // extension. + String filename = isUpdate ? existing.getName() : getFileNameForResource(name, headers); - String filename = name.endsWith(JSON_EXT) ? name : name + JSON_EXT; if (ts.deployDescriptor(filename, content)) { try { if (isUpdate) { @@ -426,6 +433,43 @@ public class TopologiesResource { } + private String getFileNameForResource(String resourceName, HttpHeaders headers) { + String filename; + String extension = FilenameUtils.getExtension(resourceName); + if (extension != null && !extension.isEmpty()) { + filename = resourceName; + } else { + extension = getExtensionForMediaType(headers.getMediaType()); + filename = (extension != null) ? (resourceName + extension) : (resourceName + JSON_EXT); + } + return filename; + } + + private String getExtensionForMediaType(MediaType type) { + String extension = null; + + for (MediaType key : mediaTypeFileExtensions.keySet()) { + if (type.isCompatible(key)) { + extension = mediaTypeFileExtensions.get(key); + break; + } + } + + return extension; + } + + private File getExistingConfigFile(Collection<File> existing, String candidateName) { + File result = null; + for (File exists : existing) { + if (FilenameUtils.getBaseName(exists.getName()).equals(candidateName)) { + result = exists; + break; + } + } + return result; + } + + private static class TopologyComparator implements Comparator<SimpleTopology> { @Override public int compare(SimpleTopology t1, SimpleTopology t2) { http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-spi/src/main/java/org/apache/knox/gateway/topology/monitor/RemoteConfigurationMonitor.java ---------------------------------------------------------------------- diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/monitor/RemoteConfigurationMonitor.java b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/monitor/RemoteConfigurationMonitor.java index 0ce1513..c9f3394 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/monitor/RemoteConfigurationMonitor.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/monitor/RemoteConfigurationMonitor.java @@ -16,9 +16,14 @@ */ package org.apache.knox.gateway.topology.monitor; +import org.apache.knox.gateway.services.config.client.RemoteConfigurationRegistryClient; + public interface RemoteConfigurationMonitor { void start() throws Exception; void stop() throws Exception; + + RemoteConfigurationRegistryClient getClient(); + } http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java ---------------------------------------------------------------------- diff --git a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java index c5d5fba..0db0121 100644 --- a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java +++ b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java @@ -1000,15 +1000,6 @@ public class GatewayAdminTopologyFuncTest { XMLTag newProviderConfigXML = createProviderConfiguration(); - // Attempt to PUT a provider config with an INCORRECT Content-type header - given() - .auth().preemptive().basic(username, password) - .header("Content-type", MediaType.APPLICATION_JSON) - .body(newProviderConfigXML.toBytes("utf-8")) - .then() - .statusCode(HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE) - .when().put(serviceUrl + "/" + newProviderConfigName); - // Attempt to PUT a provider config with the CORRECT Content-type header given() .auth().preemptive().basic(username, password)
