You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by mo...@apache.org on 2017/11/13 15:48:03 UTC

[13/16] knox git commit: Merge branch 'master' into KNOX-998-Package_Restructuring

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/TopologiesResource.java
----------------------------------------------------------------------
diff --cc gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/TopologiesResource.java
index a0035fc,0000000..9ecd7fc
mode 100644,000000..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
@@@ -1,657 -1,0 +1,674 @@@
 +/**
 + * 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.service.admin;
 +
 +import com.fasterxml.jackson.annotation.JsonProperty;
 +import org.apache.commons.io.FileUtils;
 +import org.apache.commons.io.FilenameUtils;
 +import org.apache.knox.gateway.i18n.GatewaySpiMessages;
 +import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 +import org.apache.knox.gateway.service.admin.beans.BeanConverter;
 +import org.apache.knox.gateway.service.admin.beans.Topology;
 +import org.apache.knox.gateway.services.GatewayServices;
 +import org.apache.knox.gateway.config.GatewayConfig;
 +import org.apache.knox.gateway.services.topology.TopologyService;
 +
 +import javax.servlet.http.HttpServletRequest;
 +import javax.ws.rs.Consumes;
 +import javax.ws.rs.DELETE;
 +import javax.ws.rs.GET;
 +import javax.ws.rs.PUT;
 +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.Response;
 +import javax.xml.bind.annotation.XmlAccessType;
 +import javax.xml.bind.annotation.XmlAccessorType;
 +import javax.xml.bind.annotation.XmlElement;
 +import javax.xml.bind.annotation.XmlElementWrapper;
 +import java.io.File;
 +import java.io.IOException;
 +import java.net.URI;
 +import java.net.URISyntaxException;
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.Comparator;
 +import java.util.List;
 +
 +import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 +import static javax.ws.rs.core.MediaType.APPLICATION_XML;
 +import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
 +
 +import static javax.ws.rs.core.Response.ok;
 +import static javax.ws.rs.core.Response.created;
 +import static javax.ws.rs.core.Response.notModified;
 +import static javax.ws.rs.core.Response.status;
 +
 +
 +@Path("/api/v1")
 +public class TopologiesResource {
 +
 +  private static final String XML_EXT  = ".xml";
 +  private static final String JSON_EXT = ".json";
 +
 +  private static final String TOPOLOGIES_API_PATH    = "topologies";
 +  private static final String SINGLE_TOPOLOGY_API_PATH = TOPOLOGIES_API_PATH + "/{id}";
 +  private static final String PROVIDERCONFIG_API_PATH = "providerconfig";
 +  private static final String SINGLE_PROVIDERCONFIG_API_PATH = PROVIDERCONFIG_API_PATH + "/{name}";
 +  private static final String DESCRIPTORS_API_PATH    = "descriptors";
 +  private static final String SINGLE_DESCRIPTOR_API_PATH = DESCRIPTORS_API_PATH + "/{name}";
 +
 +  private static GatewaySpiMessages log = MessagesFactory.get(GatewaySpiMessages.class);
 +
 +  @Context
 +  private HttpServletRequest request;
 +
 +  @GET
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  @Path(SINGLE_TOPOLOGY_API_PATH)
 +  public Topology getTopology(@PathParam("id") String id) {
 +    GatewayServices services = (GatewayServices) request.getServletContext()
 +        .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +    GatewayConfig config = (GatewayConfig) request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
 +
 +    TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE);
 +
 +    for (org.apache.knox.gateway.topology.Topology t : ts.getTopologies()) {
 +      if(t.getName().equals(id)) {
 +        try {
 +          t.setUri(new URI( buildURI(t, config, request) ));
 +        } catch (URISyntaxException se) {
 +          t.setUri(null);
 +        }
 +        return BeanConverter.getTopology(t);
 +      }
 +    }
 +    return null;
 +  }
 +
 +  @GET
 +  @Produces({APPLICATION_JSON, APPLICATION_XML})
 +  @Path(TOPOLOGIES_API_PATH)
 +  public SimpleTopologyWrapper getTopologies() {
 +    GatewayServices services = (GatewayServices) request.getServletContext()
 +        .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +
 +    TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE);
 +
 +    ArrayList<SimpleTopology> st = new ArrayList<SimpleTopology>();
 +    GatewayConfig conf = (GatewayConfig) request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
 +
 +    for (org.apache.knox.gateway.topology.Topology t : ts.getTopologies()) {
 +      st.add(getSimpleTopology(t, conf));
 +    }
 +
 +    Collections.sort(st, new TopologyComparator());
 +    SimpleTopologyWrapper stw = new SimpleTopologyWrapper();
 +
 +    for(SimpleTopology t : st){
 +      stw.topologies.add(t);
 +    }
 +
 +    return stw;
 +
 +  }
 +
 +  @PUT
 +  @Consumes({APPLICATION_JSON, APPLICATION_XML})
 +  @Path(SINGLE_TOPOLOGY_API_PATH)
 +  public Topology uploadTopology(@PathParam("id") String id, Topology t) {
++    Topology result = null;
 +
-     GatewayServices gs = (GatewayServices) request.getServletContext()
-         .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
++    GatewayServices gs =
++                (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +    t.setName(id);
 +    TopologyService ts = gs.getService(GatewayServices.TOPOLOGY_SERVICE);
 +
-     ts.deployTopology(BeanConverter.getTopology(t));
++    // Check for existing topology with the same name, to see if it had been generated
++    boolean existingGenerated = false;
++    for (org.apache.hadoop.gateway.topology.Topology existingTopology : ts.getTopologies()) {
++      if(existingTopology.getName().equals(id)) {
++        existingGenerated = existingTopology.isGenerated();
++        break;
++      }
++    }
++
++    // If a topology with the same ID exists, which had been generated, then DO NOT overwrite it because it will be
++    // out of sync with the source descriptor. Otherwise, deploy the updated version.
++    if (!existingGenerated) {
++      ts.deployTopology(BeanConverter.getTopology(t));
++      result = getTopology(id);
++    } else {
++      log.disallowedOverwritingGeneratedTopology(id);
++    }
 +
-     return getTopology(id);
++    return result;
 +  }
 +
 +  @DELETE
 +  @Produces(APPLICATION_JSON)
 +  @Path(SINGLE_TOPOLOGY_API_PATH)
 +  public Response deleteTopology(@PathParam("id") String id) {
 +    boolean deleted = false;
 +    if(!"admin".equals(id)) {
 +      GatewayServices services = (GatewayServices) request.getServletContext()
 +          .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +      TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE);
 +
 +      for (org.apache.knox.gateway.topology.Topology t : ts.getTopologies()) {
 +        if(t.getName().equals(id)) {
 +          ts.deleteTopology(t);
 +          deleted = true;
 +        }
 +      }
 +    }else{
 +      deleted = false;
 +    }
 +    return ok().entity("{ \"deleted\" : " + deleted + " }").build();
 +  }
 +
 +  @GET
 +  @Produces({APPLICATION_JSON})
 +  @Path(PROVIDERCONFIG_API_PATH)
 +  public HrefListing getProviderConfigurations() {
 +    HrefListing listing = new HrefListing();
 +    listing.setHref(buildHref(request));
 +
 +    GatewayServices services =
 +            (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +    List<HrefListItem> configs = new ArrayList<>();
 +    TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE);
 +    // Get all the simple descriptor file names
 +    for (File providerConfig : ts.getProviderConfigurations()){
 +      String id = FilenameUtils.getBaseName(providerConfig.getName());
 +      configs.add(new HrefListItem(buildHref(id, request), providerConfig.getName()));
 +    }
 +
 +    listing.setItems(configs);
 +    return listing;
 +  }
 +
 +  @GET
 +  @Produces({APPLICATION_XML})
 +  @Path(SINGLE_PROVIDERCONFIG_API_PATH)
 +  public Response getProviderConfiguration(@PathParam("name") String name) {
 +    Response response;
 +
 +    GatewayServices services =
 +            (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +    TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE);
 +
 +    File providerConfigFile = null;
 +
 +    for (File pc : ts.getProviderConfigurations()){
 +      // If the file name matches the specified id
 +      if (FilenameUtils.getBaseName(pc.getName()).equals(name)) {
 +        providerConfigFile = pc;
 +        break;
 +      }
 +    }
 +
 +    if (providerConfigFile != null) {
 +      byte[] content = null;
 +      try {
 +        content = FileUtils.readFileToByteArray(providerConfigFile);
 +        response = ok().entity(content).build();
 +      } catch (IOException e) {
 +        log.failedToReadConfigurationFile(providerConfigFile.getAbsolutePath(), e);
 +        response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
 +      }
 +
 +    } else {
 +      response = Response.status(Response.Status.NOT_FOUND).build();
 +    }
 +    return response;
 +  }
 +
 +  @DELETE
 +  @Produces(APPLICATION_JSON)
 +  @Path(SINGLE_PROVIDERCONFIG_API_PATH)
 +  public Response deleteProviderConfiguration(@PathParam("name") String name) {
 +    Response response;
 +    GatewayServices services =
 +            (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +    TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE);
 +    if (ts.deleteProviderConfiguration(name)) {
 +      response = ok().entity("{ \"deleted\" : \"provider config " + name + "\" }").build();
 +    } else {
 +      response = notModified().build();
 +    }
 +    return response;
 +  }
 +
 +
 +  @DELETE
 +  @Produces(APPLICATION_JSON)
 +  @Path(SINGLE_DESCRIPTOR_API_PATH)
 +  public Response deleteSimpleDescriptor(@PathParam("name") String name) {
 +    Response response = null;
 +    if(!"admin".equals(name)) {
 +      GatewayServices services =
 +              (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +      TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE);
 +      if (ts.deleteDescriptor(name)) {
 +        response = ok().entity("{ \"deleted\" : \"descriptor " + name + "\" }").build();
 +      }
 +    }
 +
 +    if (response == null) {
 +      response = notModified().build();
 +    }
 +
 +    return response;
 +  }
 +
 +
 +  @PUT
 +  @Consumes({APPLICATION_XML})
 +  @Path(SINGLE_PROVIDERCONFIG_API_PATH)
 +  public Response uploadProviderConfiguration(@PathParam("name") String name, String content) {
 +    Response response = null;
 +
 +    GatewayServices gs =
 +            (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +    TopologyService ts = gs.getService(GatewayServices.TOPOLOGY_SERVICE);
 +
 +    boolean isUpdate = configFileExists(ts.getProviderConfigurations(), name);
 +
 +    String filename = name.endsWith(XML_EXT) ? name : name + XML_EXT;
 +    if (ts.deployProviderConfiguration(filename, content)) {
 +      try {
 +        if (isUpdate) {
 +          response = Response.noContent().build();
 +        } else{
 +          response = created(new URI(buildHref(request))).build();
 +        }
 +      } catch (URISyntaxException e) {
 +        log.invalidResourceURI(e.getInput(), e.getReason(), e);
 +        response = status(Response.Status.BAD_REQUEST).entity("{ \"error\" : \"Failed to deploy provider configuration " + name + "\" }").build();
 +      }
 +    }
 +
 +    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})
 +  @Path(SINGLE_DESCRIPTOR_API_PATH)
 +  public Response uploadSimpleDescriptor(@PathParam("name") String name, String content) {
 +    Response response = null;
 +
 +    GatewayServices gs =
 +            (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +    TopologyService ts = gs.getService(GatewayServices.TOPOLOGY_SERVICE);
 +
 +    boolean isUpdate = configFileExists(ts.getDescriptors(), name);
 +
 +    String filename = name.endsWith(JSON_EXT) ? name : name + JSON_EXT;
 +    if (ts.deployDescriptor(filename, content)) {
 +      try {
 +        if (isUpdate) {
 +          response = Response.noContent().build();
 +        } else {
 +          response = created(new URI(buildHref(request))).build();
 +        }
 +      } catch (URISyntaxException e) {
 +        log.invalidResourceURI(e.getInput(), e.getReason(), e);
 +        response = status(Response.Status.BAD_REQUEST).entity("{ \"error\" : \"Failed to deploy descriptor " + name + "\" }").build();
 +      }
 +    }
 +
 +    return response;
 +  }
 +
 +
 +  @GET
 +  @Produces({APPLICATION_JSON})
 +  @Path(DESCRIPTORS_API_PATH)
 +  public HrefListing getSimpleDescriptors() {
 +    HrefListing listing = new HrefListing();
 +    listing.setHref(buildHref(request));
 +
 +    GatewayServices services =
 +            (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +    List<HrefListItem> descriptors = new ArrayList<>();
 +    TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE);
 +    for (File descriptor : ts.getDescriptors()){
 +      String id = FilenameUtils.getBaseName(descriptor.getName());
 +      descriptors.add(new HrefListItem(buildHref(id, request), descriptor.getName()));
 +    }
 +
 +    listing.setItems(descriptors);
 +    return listing;
 +  }
 +
 +
 +  @GET
 +  @Produces({APPLICATION_JSON, TEXT_PLAIN})
 +  @Path(SINGLE_DESCRIPTOR_API_PATH)
 +  public Response getSimpleDescriptor(@PathParam("name") String name) {
 +    Response response;
 +
 +    GatewayServices services =
 +            (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +
 +    TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE);
 +
 +    File descriptorFile = null;
 +
 +    for (File sd : ts.getDescriptors()){
 +      // If the file name matches the specified id
 +      if (FilenameUtils.getBaseName(sd.getName()).equals(name)) {
 +        descriptorFile = sd;
 +        break;
 +      }
 +    }
 +
 +    if (descriptorFile != null) {
 +      String mediaType = APPLICATION_JSON;
 +
 +      byte[] content = null;
 +      try {
 +        if ("yml".equals(FilenameUtils.getExtension(descriptorFile.getName()))) {
 +          mediaType = TEXT_PLAIN;
 +        }
 +        content = FileUtils.readFileToByteArray(descriptorFile);
 +        response = ok().type(mediaType).entity(content).build();
 +      } catch (IOException e) {
 +        log.failedToReadConfigurationFile(descriptorFile.getAbsolutePath(), e);
 +        response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
 +      }
 +    } else {
 +      response = Response.status(Response.Status.NOT_FOUND).build();
 +    }
 +
 +    return response;
 +  }
 +
 +
 +  private static class TopologyComparator implements Comparator<SimpleTopology> {
 +    @Override
 +    public int compare(SimpleTopology t1, SimpleTopology t2) {
 +      return t1.getName().compareTo(t2.getName());
 +    }
 +  }
 +
 +
 +  String buildURI(org.apache.knox.gateway.topology.Topology topology, GatewayConfig config, HttpServletRequest req){
 +    String uri = buildXForwardBaseURL(req);
 +
 +    // Strip extra context
 +    uri = uri.replace(req.getContextPath(), "");
 +
 +    // Add the gateway path
 +    String gatewayPath;
 +    if(config.getGatewayPath() != null){
 +      gatewayPath = config.getGatewayPath();
 +    }else{
 +      gatewayPath = "gateway";
 +    }
 +    uri += "/" + gatewayPath;
 +
 +    uri += "/" + topology.getName();
 +    return uri;
 +  }
 +
 +  String buildHref(HttpServletRequest req) {
 +    return buildHref((String)null, req);
 +  }
 +
 +  String buildHref(String id, HttpServletRequest req) {
 +    String href = buildXForwardBaseURL(req);
 +    // Make sure that the pathInfo doesn't have any '/' chars at the end.
 +    String pathInfo = req.getPathInfo();
 +    while(pathInfo.endsWith("/")) {
 +      pathInfo = pathInfo.substring(0, pathInfo.length() - 1);
 +    }
 +
 +    href += pathInfo;
 +
 +    if (id != null) {
 +      href += "/" + id;
 +    }
 +
 +    return href;
 +  }
 +
 +   String buildHref(org.apache.knox.gateway.topology.Topology t, HttpServletRequest req) {
 +     return buildHref(t.getName(), req);
 +  }
 +
 +  private SimpleTopology getSimpleTopology(org.apache.knox.gateway.topology.Topology t, GatewayConfig config) {
 +    String uri = buildURI(t, config, request);
 +    String href = buildHref(t, request);
 +    return new SimpleTopology(t, uri, href);
 +  }
 +
 +  private String buildXForwardBaseURL(HttpServletRequest req){
 +    final String X_Forwarded = "X-Forwarded-";
 +    final String X_Forwarded_Context = X_Forwarded + "Context";
 +    final String X_Forwarded_Proto = X_Forwarded + "Proto";
 +    final String X_Forwarded_Host = X_Forwarded + "Host";
 +    final String X_Forwarded_Port = X_Forwarded + "Port";
 +    final String X_Forwarded_Server = X_Forwarded + "Server";
 +
 +    String baseURL = "";
 +
 +    // Get Protocol
 +    if(req.getHeader(X_Forwarded_Proto) != null){
 +      baseURL += req.getHeader(X_Forwarded_Proto) + "://";
 +    } else {
 +      baseURL += req.getProtocol() + "://";
 +    }
 +
 +    // Handle Server/Host and Port Here
 +    if (req.getHeader(X_Forwarded_Host) != null && req.getHeader(X_Forwarded_Port) != null){
 +      // Double check to see if host has port
 +      if(req.getHeader(X_Forwarded_Host).contains(req.getHeader(X_Forwarded_Port))){
 +        baseURL += req.getHeader(X_Forwarded_Host);
 +      } else {
 +        // If there's no port, add the host and port together;
 +        baseURL += req.getHeader(X_Forwarded_Host) + ":" + req.getHeader(X_Forwarded_Port);
 +      }
 +    } else if(req.getHeader(X_Forwarded_Server) != null && req.getHeader(X_Forwarded_Port) != null){
 +      // Tack on the server and port if they're available. Try host if server not available
 +      baseURL += req.getHeader(X_Forwarded_Server) + ":" + req.getHeader(X_Forwarded_Port);
 +    } else if(req.getHeader(X_Forwarded_Port) != null) {
 +      // if we at least have a port, we can use it.
 +      baseURL += req.getServerName() + ":" + req.getHeader(X_Forwarded_Port);
 +    } else {
 +      // Resort to request members
 +      baseURL += req.getServerName() + ":" + req.getLocalPort();
 +    }
 +
 +    // Handle Server context
 +    if( req.getHeader(X_Forwarded_Context) != null ) {
 +      baseURL += req.getHeader( X_Forwarded_Context );
 +    } else {
 +      baseURL += req.getContextPath();
 +    }
 +
 +    return baseURL;
 +  }
 +
 +
 +  static class HrefListing {
 +    @JsonProperty
 +    String href;
 +
 +    @JsonProperty
 +    List<HrefListItem> items;
 +
 +    HrefListing() {}
 +
 +    public void setHref(String href) {
 +      this.href = href;
 +    }
 +
 +    public String getHref() {
 +      return href;
 +    }
 +
 +    public void setItems(List<HrefListItem> items) {
 +      this.items = items;
 +    }
 +
 +    public List<HrefListItem> getItems() {
 +      return items;
 +    }
 +  }
 +
 +  static class HrefListItem {
 +    @JsonProperty
 +    String href;
 +
 +    @JsonProperty
 +    String name;
 +
 +    HrefListItem() {}
 +
 +    HrefListItem(String href, String name) {
 +      this.href = href;
 +      this.name = name;
 +    }
 +
 +    public void setHref(String href) {
 +      this.href = href;
 +    }
 +
 +    public String getHref() {
 +      return href;
 +    }
 +
 +    public void setName(String name) {
 +      this.name = name;
 +    }
 +    public String getName() {
 +      return name;
 +    }
 +  }
 +
 +
 +  @XmlAccessorType(XmlAccessType.NONE)
 +  public static class SimpleTopology {
 +
 +    @XmlElement
 +    private String name;
 +    @XmlElement
 +    private String timestamp;
 +    @XmlElement
 +    private String defaultServicePath;
 +    @XmlElement
 +    private String uri;
 +    @XmlElement
 +    private String href;
 +
 +    public SimpleTopology() {}
 +
 +    public SimpleTopology(org.apache.knox.gateway.topology.Topology t, String uri, String href) {
 +      this.name = t.getName();
 +      this.timestamp = Long.toString(t.getTimestamp());
 +      this.defaultServicePath = t.getDefaultServicePath();
 +      this.uri = uri;
 +      this.href = href;
 +    }
 +
 +    public String getName() {
 +      return name;
 +    }
 +
 +    public void setName(String n) {
 +      name = n;
 +    }
 +
 +    public String getTimestamp() {
 +      return timestamp;
 +    }
 +
 +    public void setDefaultService(String defaultServicePath) {
 +      this.defaultServicePath = defaultServicePath;
 +    }
 +
 +    public String getDefaultService() {
 +      return defaultServicePath;
 +    }
 +
 +    public void setTimestamp(String timestamp) {
 +      this.timestamp = timestamp;
 +    }
 +
 +    public String getUri() {
 +      return uri;
 +    }
 +
 +    public void setUri(String uri) {
 +      this.uri = uri;
 +    }
 +
 +    public String getHref() {
 +      return href;
 +    }
 +
 +    public void setHref(String href) {
 +      this.href = href;
 +    }
 +  }
 +
 +  @XmlAccessorType(XmlAccessType.FIELD)
 +  public static class SimpleTopologyWrapper{
 +
 +    @XmlElement(name="topology")
 +    @XmlElementWrapper(name="topologies")
 +    private List<SimpleTopology> topologies = new ArrayList<SimpleTopology>();
 +
 +    public List<SimpleTopology> getTopologies(){
 +      return topologies;
 +    }
 +
 +    public void setTopologies(List<SimpleTopology> ts){
 +      this.topologies = ts;
 +    }
 +
 +  }
 +}
 +

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/beans/BeanConverter.java
----------------------------------------------------------------------
diff --cc gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/beans/BeanConverter.java
index e8d6915,0000000..e916568
mode 100644,000000..100644
--- a/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/beans/BeanConverter.java
+++ b/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/beans/BeanConverter.java
@@@ -1,170 -1,0 +1,172 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + * <p>
 + * http://www.apache.org/licenses/LICENSE-2.0
 + * <p>
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.service.admin.beans;
 +
 +import org.apache.knox.gateway.topology.Version;
 +
 +import java.util.Collection;
 +
 +public class BeanConverter {
 +
 +  public static Topology getTopology(
 +      org.apache.knox.gateway.topology.Topology topology) {
 +    Topology topologyResource = new Topology();
 +    topologyResource.setName(topology.getName());
 +    topologyResource.setTimestamp(topology.getTimestamp());
 +    topologyResource.setPath(topology.getDefaultServicePath());
 +    topologyResource.setUri(topology.getUri());
++    topologyResource.setGenerated(topology.isGenerated());
 +    for ( org.apache.knox.gateway.topology.Provider provider : topology.getProviders() ) {
 +      topologyResource.getProviders().add( getProvider(provider) );
 +    }
 +    for ( org.apache.knox.gateway.topology.Service service : topology.getServices() ) {
 +      topologyResource.getServices().add( getService(service) );
 +    }
 +    for ( org.apache.knox.gateway.topology.Application application : topology.getApplications() ) {
 +      topologyResource.getApplications().add( getApplication(application) );
 +    }
 +    return topologyResource;
 +  }
 +
 +  public static org.apache.knox.gateway.topology.Topology getTopology(Topology topology) {
 +    org.apache.knox.gateway.topology.Topology deploymentTopology = new org.apache.knox.gateway.topology.Topology();
 +    deploymentTopology.setName(topology.getName());
 +    deploymentTopology.setTimestamp(topology.getTimestamp());
 +    deploymentTopology.setDefaultServicePath(topology.getPath());
 +    deploymentTopology.setUri(topology.getUri());
++    deploymentTopology.setGenerated(topology.isGenerated());
 +    for ( Provider provider : topology.getProviders() ) {
 +      deploymentTopology.addProvider( getProvider(provider) );
 +    }
 +    for ( Service service : topology.getServices() ) {
 +      deploymentTopology.addService( getService(service) );
 +    }
 +    for ( Application application : topology.getApplications() ) {
 +      deploymentTopology.addApplication( getApplication(application) );
 +    }
 +    return deploymentTopology;
 +  }
 +
 +  private static Provider getProvider(
 +      org.apache.knox.gateway.topology.Provider provider) {
 +    Provider providerResource = new Provider();
 +    providerResource.setName(provider.getName());
 +    providerResource.setEnabled(provider.isEnabled());
 +    providerResource.setRole(provider.getRole());
 +    Collection<org.apache.knox.gateway.topology.Param> paramsList = provider.getParamsList();
 +    if (paramsList != null && !paramsList.isEmpty()) {
 +      for ( org.apache.knox.gateway.topology.Param param : paramsList ) {
 +        providerResource.getParams().add(getParam(param));
 +      }
 +    }
 +    return providerResource;
 +  }
 +
 +  private static org.apache.knox.gateway.topology.Provider getProvider(Provider provider) {
 +    org.apache.knox.gateway.topology.Provider deploymentProvider = new org.apache.knox.gateway.topology.Provider();
 +    deploymentProvider.setName(provider.getName());
 +    deploymentProvider.setEnabled(provider.isEnabled());
 +    deploymentProvider.setRole(provider.getRole());
 +    for ( Param param : provider.getParams() ) {
 +      deploymentProvider.addParam( getParam(param) );
 +    }
 +    return deploymentProvider;
 +  }
 +
 +  private static Service getService(
 +      org.apache.knox.gateway.topology.Service service) {
 +    Service serviceResource = new Service();
 +    serviceResource.setRole(service.getRole());
 +    serviceResource.setName(service.getName());
 +    Version version = service.getVersion();
 +    if (version != null) {
 +      serviceResource.setVersion(version.toString());
 +    }
 +    Collection<org.apache.knox.gateway.topology.Param> paramsList = service.getParamsList();
 +    if (paramsList != null && !paramsList.isEmpty()) {
 +      for ( org.apache.knox.gateway.topology.Param param : paramsList ) {
 +        serviceResource.getParams().add(getParam(param));
 +      }
 +    }
 +    for ( String url : service.getUrls() ) {
 +      serviceResource.getUrls().add( url );
 +    }
 +    return serviceResource;
 +  }
 +
 +  private static org.apache.knox.gateway.topology.Service getService(Service service) {
 +    org.apache.knox.gateway.topology.Service deploymentService = new org.apache.knox.gateway.topology.Service();
 +    deploymentService.setRole(service.getRole());
 +    deploymentService.setName(service.getName());
 +    if (service.getVersion() != null) {
 +      deploymentService.setVersion(new Version(service.getVersion()));
 +    }
 +    for ( Param param : service.getParams() ) {
 +      deploymentService.addParam( getParam(param) );
 +    }
 +    for ( String url : service.getUrls() ) {
 +      deploymentService.addUrl( url );
 +    }
 +    return deploymentService;
 +  }
 +
 +  private static Application getApplication(
 +      org.apache.knox.gateway.topology.Application application) {
 +    Application applicationResource = new Application();
 +    applicationResource.setRole(application.getRole());
 +    applicationResource.setName(application.getName());
 +    Version version = application.getVersion();
 +    if (version != null) {
 +      applicationResource.setVersion(version.toString());
 +    }
 +    Collection<org.apache.knox.gateway.topology.Param> paramsList = application.getParamsList();
 +    if (paramsList != null && !paramsList.isEmpty()) {
 +      for ( org.apache.knox.gateway.topology.Param param : paramsList ) {
 +        applicationResource.getParams().add(getParam(param));
 +      }
 +    }
 +    for ( String url : application.getUrls() ) {
 +      applicationResource.getUrls().add( url );
 +    }
 +    return applicationResource;
 +  }
 +
 +  private static org.apache.knox.gateway.topology.Application getApplication(Application application) {
 +    org.apache.knox.gateway.topology.Application applicationResource = new org.apache.knox.gateway.topology.Application();
 +    applicationResource.setRole(application.getRole());
 +    applicationResource.setName(application.getName());
 +    if (application.getVersion() != null) {
 +      applicationResource.setVersion(new Version(application.getVersion()));
 +    }
 +    for ( Param param : application.getParams() ) {
 +      applicationResource.addParam( getParam(param) );
 +    }
 +    for ( String url : application.getUrls() ) {
 +      applicationResource.getUrls().add( url );
 +    }
 +    return applicationResource;
 +  }
 +
 +  private static Param getParam(org.apache.knox.gateway.topology.Param param) {
 +    return new Param(param.getName(), param.getValue());
 +  }
 +
 +  private static org.apache.knox.gateway.topology.Param getParam(Param param) {
 +    return new org.apache.knox.gateway.topology.Param(param.getName(), param.getValue());
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/beans/Topology.java
----------------------------------------------------------------------
diff --cc gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/beans/Topology.java
index 2d2eab8,0000000..e1a8279
mode 100644,000000..100644
--- a/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/beans/Topology.java
+++ b/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/beans/Topology.java
@@@ -1,119 -1,0 +1,130 @@@
 +/**
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + * <p>
 + * http://www.apache.org/licenses/LICENSE-2.0
 + * <p>
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.knox.gateway.service.admin.beans;
 +
 +import javax.xml.bind.annotation.XmlElement;
 +import javax.xml.bind.annotation.XmlElementWrapper;
 +import javax.xml.bind.annotation.XmlRootElement;
 +import java.net.URI;
 +import java.util.ArrayList;
 +import java.util.List;
 +
 +@XmlRootElement(name="topology")
 +public class Topology {
 +
 +  @XmlElement
 +  private URI uri;
 +
 +  @XmlElement
 +  private String name;
 +
 +  @XmlElement
 +  private String path;
 +
 +  @XmlElement
 +  private long timestamp;
 +
++  @XmlElement(name="generated")
++  private boolean isGenerated;
++
 +  @XmlElement(name="provider")
 +  @XmlElementWrapper(name="gateway")
 +  public List<Provider> providers;
 +
 +  @XmlElement(name="service")
 +  public List<Service> services;
 +
 +  @XmlElement(name="application")
 +  private List<Application> applications;
 +
 +  public Topology() {
 +  }
 +
 +  public URI getUri() {
 +    return uri;
 +  }
 +
 +  public void setUri( URI uri ) {
 +    this.uri = uri;
 +  }
 +
 +  public String getName() {
 +    return name;
 +  }
 +
 +  public void setName( String name ) {
 +    this.name = name;
 +  }
 +
 +  public long getTimestamp() {
 +    return timestamp;
 +  }
 +
 +  public void setPath( String defaultServicePath ) {
 +    this.path = defaultServicePath;
 +  }
 +
 +  public String getPath() {
 +    return path;
 +  }
 +
 +  public void setTimestamp( long timestamp ) {
 +    this.timestamp = timestamp;
 +  }
 +
++  public boolean isGenerated() {
++    return isGenerated;
++  }
++
++  public void setGenerated(boolean isGenerated) {
++    this.isGenerated = isGenerated;
++  }
++
 +  public List<Service> getServices() {
 +    if (services == null) {
 +      services = new ArrayList<>();
 +    }
 +    return services;
 +  }
 +
 +  public List<Application> getApplications() {
 +    if (applications == null) {
 +      applications = new ArrayList<>();
 +    }
 +    return applications;
 +  }
 +
 +  public List<Provider> getProviders() {
 +    if (providers == null) {
 +      providers = new ArrayList<>();
 +    }
 +    return providers;
 +  }
 +
 +  public void setProviders(List<Provider> providers) {
 +    this.providers = providers;
 +  }
 +
 +  public void setServices(List<Service> services) {
 +    this.services = services;
 +  }
 +
 +  public void setApplications(List<Application> applications) {
 +    this.applications = applications;
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-service-definitions/src/main/java/org/apache/knox/gateway/service/definition/CustomDispatch.java
----------------------------------------------------------------------
diff --cc gateway-service-definitions/src/main/java/org/apache/knox/gateway/service/definition/CustomDispatch.java
index 3fe81e8,0000000..ac82b39
mode 100644,000000..100644
--- a/gateway-service-definitions/src/main/java/org/apache/knox/gateway/service/definition/CustomDispatch.java
+++ b/gateway-service-definitions/src/main/java/org/apache/knox/gateway/service/definition/CustomDispatch.java
@@@ -1,80 -1,0 +1,91 @@@
 +/**
 + * 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.service.definition;
 +
 +import javax.xml.bind.annotation.XmlAttribute;
 +import javax.xml.bind.annotation.XmlType;
 +
 +@XmlType(name = "dispatch")
 +public class CustomDispatch {
 +
 +  private String contributorName;
 +
 +  private String haContributorName;
 +
 +  private String className;
 +
 +  private String haClassName;
 +
 +  private String httpClientFactory;
 +
++  private boolean useTwoWaySsl = false;
++
 +  @XmlAttribute(name = "contributor-name")
 +  public String getContributorName() {
 +    return contributorName;
 +  }
 +
 +  public void setContributorName(String contributorName) {
 +    this.contributorName = contributorName;
 +  }
 +
 +  @XmlAttribute(name = "ha-contributor-name")
 +  public String getHaContributorName() {
 +    return haContributorName;
 +  }
 +
 +  public void setHaContributorName(String haContributorName) {
 +    this.haContributorName = haContributorName;
 +  }
 +
 +  @XmlAttribute(name = "classname")
 +  public String getClassName() {
 +    return className;
 +  }
 +
 +  public void setClassName(String className) {
 +    this.className = className;
 +  }
 +
 +  @XmlAttribute(name = "ha-classname")
 +  public String getHaClassName() {
 +    return haClassName;
 +  }
 +
 +  public void setHaClassName(String haContributorClassName) {
 +    this.haClassName = haContributorClassName;
 +  }
 +
 +  @XmlAttribute(name = "http-client-factory")
 +  public String getHttpClientFactory() {
 +    return httpClientFactory;
 +  }
 +
 +  public void setHttpClientFactory(String httpClientFactory) {
 +    this.httpClientFactory = httpClientFactory;
 +  }
++
++  @XmlAttribute(name = "use-two-way-ssl")
++  public boolean getUseTwoWaySsl() {
++    return useTwoWaySsl;
++  }
++
++  public void setUseTwoWaySsl(boolean useTwoWaySsl) {
++    this.useTwoWaySsl = useTwoWaySsl;
++  }
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-shell-launcher/pom.xml
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-shell-release/pom.xml
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java
index e822364,0000000..dcb7465
mode 100644,000000..100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java
@@@ -1,233 -1,0 +1,270 @@@
 +/**
 + * 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.dispatch;
 +
++import java.io.IOException;
++import java.security.KeyStore;
++import java.security.Principal;
++import java.util.Collections;
++import java.util.Date;
++import java.util.List;
++
++import javax.net.ssl.SSLContext;
++import javax.servlet.FilterConfig;
++
++import org.apache.knox.gateway.services.security.AliasService;
++import org.apache.knox.gateway.services.security.AliasServiceException;
++import org.apache.knox.gateway.services.security.KeystoreService;
++import org.apache.knox.gateway.services.security.MasterService;
 +import org.apache.knox.gateway.config.GatewayConfig;
 +import org.apache.knox.gateway.services.GatewayServices;
 +import org.apache.knox.gateway.services.metrics.MetricsService;
 +import org.apache.http.HttpRequest;
 +import org.apache.http.HttpResponse;
 +import org.apache.http.ProtocolException;
 +import org.apache.http.auth.AuthSchemeProvider;
 +import org.apache.http.auth.AuthScope;
 +import org.apache.http.auth.Credentials;
 +import org.apache.http.client.CookieStore;
 +import org.apache.http.client.CredentialsProvider;
 +import org.apache.http.client.HttpClient;
 +import org.apache.http.client.HttpRequestRetryHandler;
 +import org.apache.http.client.RedirectStrategy;
 +import org.apache.http.client.config.AuthSchemes;
 +import org.apache.http.client.config.RequestConfig;
 +import org.apache.http.client.methods.HttpUriRequest;
 +import org.apache.http.config.Registry;
 +import org.apache.http.config.RegistryBuilder;
++import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
++import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
 +import org.apache.http.cookie.Cookie;
 +import org.apache.http.impl.DefaultConnectionReuseStrategy;
 +import org.apache.http.impl.client.BasicCredentialsProvider;
 +import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
 +import org.apache.http.impl.client.HttpClientBuilder;
 +import org.apache.http.impl.client.HttpClients;
 +import org.apache.http.protocol.HttpContext;
++import org.apache.http.ssl.SSLContexts;
 +import org.joda.time.Period;
 +import org.joda.time.format.PeriodFormatter;
 +import org.joda.time.format.PeriodFormatterBuilder;
 +
- import javax.servlet.FilterConfig;
- import java.io.IOException;
- import java.security.Principal;
- import java.util.Collections;
- import java.util.Date;
- import java.util.List;
- 
 +public class DefaultHttpClientFactory implements HttpClientFactory {
 +
 +  @Override
 +  public HttpClient createHttpClient(FilterConfig filterConfig) {
 +    HttpClientBuilder builder = null;
 +    GatewayConfig gatewayConfig = (GatewayConfig) filterConfig.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
++    GatewayServices services = (GatewayServices) filterConfig.getServletContext()
++        .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +    if (gatewayConfig != null && gatewayConfig.isMetricsEnabled()) {
-       GatewayServices services = (GatewayServices) filterConfig.getServletContext()
-           .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
 +      MetricsService metricsService = services.getService(GatewayServices.METRICS_SERVICE);
 +      builder = metricsService.getInstrumented(HttpClientBuilder.class);
 +    } else {
 +      builder = HttpClients.custom();
 +    }
++    if (Boolean.parseBoolean(filterConfig.getInitParameter("useTwoWaySsl"))) {
++      char[] keypass = null;
++      MasterService ms = services.getService("MasterService");
++      AliasService as = services.getService(GatewayServices.ALIAS_SERVICE);
++      try {
++        keypass = as.getGatewayIdentityPassphrase();
++      } catch (AliasServiceException e) {
++        // nop - default passphrase will be used
++      }
++      if (keypass == null) {
++        // there has been no alias created for the key - let's assume it is the same as the keystore password
++        keypass = ms.getMasterSecret();
++      }
++
++      KeystoreService ks = services.getService(GatewayServices.KEYSTORE_SERVICE);
++      final SSLContext sslcontext;
++      try {
++        KeyStore keystoreForGateway = ks.getKeystoreForGateway();
++        sslcontext = SSLContexts.custom()
++            .loadTrustMaterial(keystoreForGateway, new TrustSelfSignedStrategy())
++            .loadKeyMaterial(keystoreForGateway, keypass)
++            .build();
++      } catch (Exception e) {
++        throw new IllegalArgumentException("Unable to create SSLContext", e);
++      }
++      builder.setSSLSocketFactory(new SSLConnectionSocketFactory(sslcontext));
++    }
 +    if ( "true".equals(System.getProperty(GatewayConfig.HADOOP_KERBEROS_SECURED)) ) {
 +      CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
 +      credentialsProvider.setCredentials(AuthScope.ANY, new UseJaasCredentials());
 +
 +      Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
 +          .register(AuthSchemes.SPNEGO, new KnoxSpnegoAuthSchemeFactory(true))
 +          .build();
 +
 +      builder = builder.setDefaultAuthSchemeRegistry(authSchemeRegistry)
 +          .setDefaultCookieStore(new HadoopAuthCookieStore())
 +          .setDefaultCredentialsProvider(credentialsProvider);
 +    } else {
 +      builder = builder.setDefaultCookieStore(new NoCookieStore());
 +    }
 +
 +    builder.setKeepAliveStrategy( DefaultConnectionKeepAliveStrategy.INSTANCE );
 +    builder.setConnectionReuseStrategy( DefaultConnectionReuseStrategy.INSTANCE );
 +    builder.setRedirectStrategy( new NeverRedirectStrategy() );
 +    builder.setRetryHandler( new NeverRetryHandler() );
 +
 +    int maxConnections = getMaxConnections( filterConfig );
 +    builder.setMaxConnTotal( maxConnections );
 +    builder.setMaxConnPerRoute( maxConnections );
 +
 +    builder.setDefaultRequestConfig( getRequestConfig( filterConfig ) );
 +
 +    HttpClient client = builder.build();
 +    return client;
 +  }
 +
 +  private static RequestConfig getRequestConfig( FilterConfig config ) {
 +    RequestConfig.Builder builder = RequestConfig.custom();
 +    int connectionTimeout = getConnectionTimeout( config );
 +    if ( connectionTimeout != -1 ) {
 +      builder.setConnectTimeout( connectionTimeout );
 +      builder.setConnectionRequestTimeout( connectionTimeout );
 +    }
 +    int socketTimeout = getSocketTimeout( config );
 +    if( socketTimeout != -1 ) {
 +      builder.setSocketTimeout( socketTimeout );
 +    }
 +    return builder.build();
 +  }
 +
 +  private static class NoCookieStore implements CookieStore {
 +    @Override
 +    public void addCookie(Cookie cookie) {
 +      //no op
 +    }
 +
 +    @Override
 +    public List<Cookie> getCookies() {
 +      return Collections.emptyList();
 +    }
 +
 +    @Override
 +    public boolean clearExpired(Date date) {
 +      return true;
 +    }
 +
 +    @Override
 +    public void clear() {
 +      //no op
 +    }
 +  }
 +
 +  private static class NeverRedirectStrategy implements RedirectStrategy {
 +    @Override
 +    public boolean isRedirected( HttpRequest request, HttpResponse response, HttpContext context )
 +        throws ProtocolException {
 +      return false;
 +    }
 +
 +    @Override
 +    public HttpUriRequest getRedirect( HttpRequest request, HttpResponse response, HttpContext context )
 +        throws ProtocolException {
 +      return null;
 +    }
 +  }
 +
 +  private static class NeverRetryHandler implements HttpRequestRetryHandler {
 +    @Override
 +    public boolean retryRequest( IOException exception, int executionCount, HttpContext context ) {
 +      return false;
 +    }
 +  }
 +
 +  private static class UseJaasCredentials implements Credentials {
 +
 +    public String getPassword() {
 +      return null;
 +    }
 +
 +    public Principal getUserPrincipal() {
 +      return null;
 +    }
 +
 +  }
 +
 +  private int getMaxConnections( FilterConfig filterConfig ) {
 +    int maxConnections = 32;
 +    GatewayConfig config =
 +        (GatewayConfig)filterConfig.getServletContext().getAttribute( GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE );
 +    if( config != null ) {
 +      maxConnections = config.getHttpClientMaxConnections();
 +    }
 +    String str = filterConfig.getInitParameter( "httpclient.maxConnections" );
 +    if( str != null ) {
 +      try {
 +        maxConnections = Integer.parseInt( str );
 +      } catch ( NumberFormatException e ) {
 +        // Ignore it and use the default.
 +      }
 +    }
 +    return maxConnections;
 +  }
 +
 +  private static int getConnectionTimeout( FilterConfig filterConfig ) {
 +    int timeout = -1;
 +    GatewayConfig globalConfig =
 +        (GatewayConfig)filterConfig.getServletContext().getAttribute( GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE );
 +    if( globalConfig != null ) {
 +      timeout = globalConfig.getHttpClientConnectionTimeout();
 +    }
 +    String str = filterConfig.getInitParameter( "httpclient.connectionTimeout" );
 +    if( str != null ) {
 +      try {
 +        timeout = (int)parseTimeout( str );
 +      } catch ( Exception e ) {
 +        // Ignore it and use the default.
 +      }
 +    }
 +    return timeout;
 +  }
 +
 +  private static int getSocketTimeout( FilterConfig filterConfig ) {
 +    int timeout = -1;
 +    GatewayConfig globalConfig =
 +        (GatewayConfig)filterConfig.getServletContext().getAttribute( GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE );
 +    if( globalConfig != null ) {
 +      timeout = globalConfig.getHttpClientSocketTimeout();
 +    }
 +    String str = filterConfig.getInitParameter( "httpclient.socketTimeout" );
 +    if( str != null ) {
 +      try {
 +        timeout = (int)parseTimeout( str );
 +      } catch ( Exception e ) {
 +        // Ignore it and use the default.
 +      }
 +    }
 +    return timeout;
 +  }
 +
 +  private static long parseTimeout( String s ) {
 +    PeriodFormatter f = new PeriodFormatterBuilder()
 +        .appendMinutes().appendSuffix("m"," min")
 +        .appendSeconds().appendSuffix("s"," sec")
 +        .appendMillis().toFormatter();
 +    Period p = Period.parse( s, f );
 +    return p.toStandardDuration().getMillis();
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
index 27a1905,0000000..42d69d9
mode 100644,000000..100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
@@@ -1,91 -1,0 +1,94 @@@
 +/**
 + * 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.i18n;
 +
 +import org.apache.knox.gateway.i18n.messages.Message;
 +import org.apache.knox.gateway.i18n.messages.MessageLevel;
 +import org.apache.knox.gateway.i18n.messages.Messages;
 +import org.apache.knox.gateway.i18n.messages.StackTrace;
 +
 +@Messages(logger="org.apache.knox.gateway")
 +public interface GatewaySpiMessages {
 +
 +  @Message(level = MessageLevel.ERROR, text = "Failed to load the internal principal mapping table: {0}" )
 +  void failedToLoadPrincipalMappingTable( @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +
 +  @Message( level = MessageLevel.ERROR, text = "Failed to execute filter: {0}" )
 +  void failedToExecuteFilter( @StackTrace( level = MessageLevel.DEBUG ) Throwable t );
 +  
 +  @Message( level = MessageLevel.ERROR, text = "Failed to encrypt passphrase: {0}" )
 +  void failedToEncryptPassphrase( @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +
 +  @Message( level = MessageLevel.ERROR, text = "Failed to generate secret key from password: {0}" )
 +  void failedToGenerateKeyFromPassword( @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +  
 +  @Message( level = MessageLevel.ERROR, text = "Failed to create keystore [filename={0}, type={1}]: {2}" )
 +  void failedToCreateKeystore( String fileName, String keyStoreType, @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +  
 +  @Message( level = MessageLevel.ERROR, text = "Failed to load keystore [filename={0}, type={1}]: {2}" )
 +  void failedToLoadKeystore( String fileName, String keyStoreType, @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +  
 +  @Message( level = MessageLevel.ERROR, text = "Failed to add credential: {1}" )
 +  void failedToAddCredential( @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +
 +  @Message(level = MessageLevel.ERROR, text = "Failed to remove credential: {1}")
 +  void failedToRemoveCredential(@StackTrace(level = MessageLevel.DEBUG) Exception e);
 +
 +  @Message( level = MessageLevel.ERROR, text = "Failed to get credential: {1}" )
 +  void failedToGetCredential(@StackTrace( level = MessageLevel.DEBUG ) Exception e);
 +  
 +  @Message( level = MessageLevel.ERROR, text = "Failed to persist master secret: {0}" )
 +  void failedToPersistMasterSecret( @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +
 +  @Message( level = MessageLevel.ERROR, text = "Failed to encrypt master secret: {0}" )
 +  void failedToEncryptMasterSecret( @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +
 +  @Message( level = MessageLevel.ERROR, text = "Failed to initialize master service from persistent master {0}: {1}" )
 +  void failedToInitializeFromPersistentMaster( String masterFileName, @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +
 +  @Message( level = MessageLevel.ERROR, text = "Failed to add self signed certificate for Gateway {0}: {1}" )
 +  void failedToAddSeflSignedCertForGateway( String alias, @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 +
 +  @Message( level = MessageLevel.ERROR, text = "Failed to get key {0}: {1}" )
 +  void failedToGetKey(String alias, @StackTrace( level = MessageLevel.DEBUG ) Exception e);
 +
 +  @Message( level = MessageLevel.DEBUG, text = "Loading from persistent master: {0}" )
 +  void loadingFromPersistentMaster( String tag );
 +
 +  @Message( level = MessageLevel.DEBUG, text = "ALIAS: {0}" )
 +  void printClusterAlias( String alias );
 +
 +  @Message( level = MessageLevel.DEBUG, text = "MASTER SERVICE == NULL: {0}" )
 +  void printMasterServiceIsNull( boolean masterServiceIsNull );
 +
 +  @Message( level = MessageLevel.ERROR, text = "Gateway has failed to start. Unable to prompt user for master secret setup. Please consider using knoxcli.sh create-master" )
 +  void unableToPromptForMasterUseKnoxCLI();
 +
 +  @Message( level = MessageLevel.ERROR, text = "Error in generating certificate: {0}" )
 +  void failedToGenerateCertificate( @StackTrace( level = MessageLevel.ERROR ) Exception e );
 +
 +  @Message(level = MessageLevel.ERROR, text = "Failed to read configuration: {0}")
 +  void failedToReadConfigurationFile(final String filePath, @StackTrace(level = MessageLevel.DEBUG) Exception e );
 +
 +  @Message(level = MessageLevel.ERROR, text = "Invalid resource URI {0} : {1}")
 +  void invalidResourceURI(final String uri, final String reason, @StackTrace(level = MessageLevel.DEBUG) Exception e );
 +
++  @Message( level = MessageLevel.ERROR, text = "Topology {0} cannot be manually overwritten because it was generated from a simple descriptor." )
++  void disallowedOverwritingGeneratedTopology(final String topologyName);
++
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-spi/src/main/java/org/apache/knox/gateway/topology/Topology.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/topology/Topology.java
index 815c218,0000000..e46197d
mode 100644,000000..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
@@@ -1,151 -1,0 +1,160 @@@
 +/**
 + * 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.apache.commons.collections.map.HashedMap;
 +import org.apache.commons.collections.map.MultiKeyMap;
 +
 +import java.net.URI;
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +
 +public class Topology {
 +
 +  private URI uri;
 +  private String name;
 +  private String defaultServicePath = null;
 +  private long timestamp;
++  private boolean isGenerated;
 +  public List<Provider> providerList = new ArrayList<Provider>();
 +  private Map<String,Map<String,Provider>> providerMap = new HashMap<>();
 +  public List<Service> services = new ArrayList<Service>();
 +  private MultiKeyMap serviceMap;
 +  private List<Application> applications = new ArrayList<Application>();
 +  private Map<String,Application> applicationMap = new HashMap<>();
 +
 +  public Topology() {
 +    serviceMap = MultiKeyMap.decorate(new HashedMap());
 +  }
 +
 +  public URI getUri() {
 +    return uri;
 +  }
 +
 +  public void setUri( URI uri ) {
 +    this.uri = uri;
 +  }
 +
 +  public String getName() {
 +    return name;
 +  }
 +
 +  public void setName( String name ) {
 +    this.name = name;
 +  }
 +
 +  public long getTimestamp() {
 +    return timestamp;
 +  }
 +
 +  public void setTimestamp( long timestamp ) {
 +    this.timestamp = timestamp;
 +  }
 +
 +  public String getDefaultServicePath() {
 +    return defaultServicePath;
 +  }
 +
 +  public void setDefaultServicePath(String servicePath) {
 +    defaultServicePath = servicePath;
 +  }
 +
++  public void setGenerated(boolean isGenerated) {
++    this.isGenerated = isGenerated;
++  }
++
++  public boolean isGenerated() {
++    return isGenerated;
++  }
++
 +  public Collection<Service> getServices() {
 +    return services;
 +  }
 +
 +  public Service getService( String role, String name, Version version) {
 +    return (Service)serviceMap.get(role, name, version);
 +  }
 +
 +  public void addService( Service service ) {
 +    services.add( service );
 +    serviceMap.put(service.getRole(), service.getName(), service.getVersion(), service);
 +  }
 +
 +  public Collection<Application> getApplications() {
 +    return applications;
 +  }
 +
 +  private static String fixApplicationUrl( String url ) {
 +    if( url == null ) {
 +      url = "/";
 +    }
 +    if( !url.startsWith( "/" ) ) {
 +      url = "/" + url;
 +    }
 +    return url;
 +  }
 +
 +  public Application getApplication(String url) {
 +    return applicationMap.get( fixApplicationUrl( url ) );
 +  }
 +
 +  public void addApplication( Application application ) {
 +    applications.add( application );
 +    List<String> urls = application.getUrls();
 +    if( urls == null || urls.isEmpty() ) {
 +      applicationMap.put( fixApplicationUrl( application.getName() ), application );
 +    } else {
 +      for( String url : application.getUrls() ) {
 +        applicationMap.put( fixApplicationUrl( url ), application );
 +      }
 +    }
 +  }
 +
 +  public Collection<Provider> getProviders() {
 +    return providerList;
 +  }
 +
 +  public Provider getProvider( String role, String name ) {
 +    Provider provider = null;
 +    Map<String,Provider> nameMap = providerMap.get( role );
 +    if( nameMap != null) { 
 +      if( name != null ) {
 +        provider = nameMap.get( name );
 +      }
 +      else {
 +        provider = (Provider) nameMap.values().toArray()[0];
 +      }
 +    }
 +    return provider;
 +  }
 +
 +  public void addProvider( Provider provider ) {
 +    providerList.add( provider );
 +    String role = provider.getRole();
 +    Map<String,Provider> nameMap = providerMap.get( role );
 +    if( nameMap == null ) {
 +      nameMap = new HashMap<>();
 +      providerMap.put( role, nameMap );
 +    }
 +    nameMap.put( provider.getName(), provider );
 +  }
 +
 +}