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/02 18:48:21 UTC

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

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/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 1861490,0000000..948447b
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,312 -1,0 +1,657 @@@
 +/**
 + * 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("topologies/{id}")
++  @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")
++  @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("topologies/{id}")
++  @Path(SINGLE_TOPOLOGY_API_PATH)
 +  public Topology uploadTopology(@PathParam("id") String id, Topology t) {
 +
 +    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));
 +
 +    return getTopology(id);
 +  }
 +
 +  @DELETE
 +  @Produces(APPLICATION_JSON)
-   @Path("topologies/{id}")
++  @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 buildURI(org.apache.knox.gateway.topology.Topology topology, GatewayConfig config, HttpServletRequest req){
 +    String uri = buildXForwardBaseURL(req);
 +
- //    Strip extra context
++    // Strip extra context
 +    uri = uri.replace(req.getContextPath(), "");
 +
- //    Add the gateway path
++    // 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(org.apache.knox.gateway.topology.Topology t, HttpServletRequest req) {
++  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.
++    // Make sure that the pathInfo doesn't have any '/' chars at the end.
 +    String pathInfo = req.getPathInfo();
-     if(pathInfo.endsWith("/")) {
-       while(pathInfo.endsWith("/")) {
-         pathInfo = pathInfo.substring(0, pathInfo.length() - 1);
-       }
++    while(pathInfo.endsWith("/")) {
++      pathInfo = pathInfo.substring(0, pathInfo.length() - 1);
++    }
++
++    href += pathInfo;
++
++    if (id != null) {
++      href += "/" + id;
 +    }
 +
-     href += pathInfo + "/" + t.getName();
 +    return href;
 +  }
 +
-   private SimpleTopology getSimpleTopology(
-       org.apache.knox.gateway.topology.Topology t, GatewayConfig config) {
++   String buildHref(org.apache.knox.gateway.topology.Topology t, HttpServletRequest req) {
++     return buildHref(t.getName(), req);
++  }
++
++  private SimpleTopology getSimpleTopology(org.apache.hadoop.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
++    // 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
++    // 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
++      // 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;
++        // 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
++      // 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.
++      // if we at least have a port, we can use it.
 +      baseURL += req.getServerName() + ":" + req.getHeader(X_Forwarded_Port);
 +    } else {
- //      Resort to request members
++      // Resort to request members
 +      baseURL += req.getServerName() + ":" + req.getLocalPort();
 +    }
 +
- //    Handle Server context
++    // 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/c754cc06/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 358b5b5,0000000..e8d6915
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,168 -1,0 +1,170 @@@
 +/**
 + * 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());
 +    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());
 +    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/c754cc06/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 9c58ad3,0000000..2d2eab8
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,108 -1,0 +1,119 @@@
 +/**
 + * 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="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 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/c754cc06/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 243bac3,0000000..27a1905
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,85 -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.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 = "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 );
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-spi/src/main/java/org/apache/knox/gateway/services/topology/TopologyService.java
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/java/org/apache/knox/gateway/services/topology/TopologyService.java
index 820da73,0000000..3be3a4a
mode 100644,000000..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
@@@ -1,50 -1,0 +1,63 @@@
 +/**
 + * 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.services.topology;
 +
 +import org.apache.knox.gateway.config.GatewayConfig;
 +import org.apache.knox.gateway.services.Service;
 +import org.apache.knox.gateway.topology.Topology;
 +import org.apache.knox.gateway.topology.TopologyListener;
 +
++import java.io.File;
 +import java.util.Collection;
 +import java.util.List;
 +import java.util.Map;
 +
 +
 +public interface TopologyService extends Service {
 +
-   public void reloadTopologies();
++  void reloadTopologies();
 +
-   public void deployTopology(Topology t);
++  void deployTopology(Topology t);
 +
-   public void redeployTopologies(String topologyName);
++  void redeployTopologies(String topologyName);
 +
-   public void addTopologyChangeListener(TopologyListener listener);
++  void addTopologyChangeListener(TopologyListener listener);
 +
-   public void startMonitor() throws Exception;
++  void startMonitor() throws Exception;
 +
-   public void stopMonitor() throws Exception;
++  void stopMonitor() throws Exception;
 +
-   public Collection<Topology> getTopologies();
++  Collection<Topology> getTopologies();
 +
-   public void deleteTopology(Topology t);
++  boolean deployProviderConfiguration(String name, String content);
 +
-   public Map<String, List<String>> getServiceTestURLs(Topology t, GatewayConfig config);
++  Collection<File> getProviderConfigurations();
 +
-   }
++  boolean deployDescriptor(String name, String content);
++
++  Collection<File> getDescriptors();
++
++  void deleteTopology(Topology t);
++
++  boolean deleteDescriptor(String name);
++
++  boolean deleteProviderConfiguration(String name);
++
++  Map<String, List<String>> getServiceTestURLs(Topology t, GatewayConfig config);
++
++}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/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 6eac50b,0000000..815c218
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,142 -1,0 +1,151 @@@
 +/**
 + * 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;
 +  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 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 );
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/c754cc06/gateway-spi/src/main/resources/org/apache/knox/gateway/topology/topology_binding-xml.xml
----------------------------------------------------------------------
diff --cc gateway-spi/src/main/resources/org/apache/knox/gateway/topology/topology_binding-xml.xml
index 9e9c26f,0000000..956387e
mode 100644,000000..100644
--- a/gateway-spi/src/main/resources/org/apache/knox/gateway/topology/topology_binding-xml.xml
+++ b/gateway-spi/src/main/resources/org/apache/knox/gateway/topology/topology_binding-xml.xml
@@@ -1,63 -1,0 +1,64 @@@
 +<?xml version="1.0"?>
 +<!--
 + 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.
 +-->
 +<xml-bindings
 +    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
 +    package-name="org.apache.knox.gateway.topology"
 +    xml-mapping-metadata-complete="true">
 +    <xml-schema
 +        element-form-default="QUALIFIED"/>
 +    <java-types>
 +        <java-type name="Topology" xml-accessor-type="NONE">
-             <xml-type prop-order="name providers services applications"/>
++            <xml-type prop-order="name defaultServicePath providers services applications"/>
 +            <xml-root-element/>
 +            <java-attributes>
 +                <xml-element java-attribute="name" name="name"/>
++                <xml-element java-attribute="defaultServicePath" name="path"/>
 +                <xml-elements java-attribute="providers">
 +                    <xml-element name="provider"/>
 +                    <xml-element-wrapper name="gateway"/>
 +                </xml-elements>
 +                <xml-element java-attribute="services" name="service"/>
 +                <xml-element java-attribute="applications" name="application"/>
 +            </java-attributes>
 +        </java-type>
 +        <java-type name="Provider" xml-accessor-type="NONE">
 +            <java-attributes>
 +                <xml-element java-attribute="name" name="name"/>
 +                <xml-element java-attribute="enabled" name="enabled"/>
 +                <xml-element java-attribute="role" name="role"/>
 +                <xml-element java-attribute="paramsList" name="param"/>
 +            </java-attributes>
 +        </java-type>
 +        <java-type name="Service" xml-accessor-type="NONE">
 +            <java-attributes>
 +                <xml-element java-attribute="name" name="name"/>
 +                <xml-element java-attribute="role" name="role"/>
 +                <xml-element java-attribute="urls" name="url"/>
 +                <xml-element java-attribute="paramsList" name="param"/>
 +            </java-attributes>
 +        </java-type>
 +        <java-type name="Application" xml-accessor-type="NONE"/>
 +        <java-type name="Param" xml-accessor-type="NONE">
 +            <java-attributes>
 +                <xml-element java-attribute="name"/>
 +                <xml-element java-attribute="value"/>
 +            </java-attributes>
 +        </java-type>
 +    </java-types>
- </xml-bindings>
++</xml-bindings>