http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorImpl.java
----------------------------------------------------------------------
diff --cc 
gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorImpl.java
index 4eb1954,0000000..f3288fd
mode 100644,000000..100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorImpl.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorImpl.java
@@@ -1,123 -1,0 +1,163 @@@
 +/**
 + * 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.simple;
 +
 +import com.fasterxml.jackson.annotation.JsonProperty;
 +
 +import java.util.ArrayList;
 +import java.util.List;
 +import java.util.Map;
 +
 +class SimpleDescriptorImpl implements SimpleDescriptor {
 +
 +    @JsonProperty("discovery-type")
 +    private String discoveryType;
 +
 +    @JsonProperty("discovery-address")
 +    private String discoveryAddress;
 +
 +    @JsonProperty("discovery-user")
 +    private String discoveryUser;
 +
 +    @JsonProperty("discovery-pwd-alias")
 +    private String discoveryPasswordAlias;
 +
 +    @JsonProperty("provider-config-ref")
 +    private String providerConfig;
 +
 +    @JsonProperty("cluster")
 +    private String cluster;
 +
 +    @JsonProperty("services")
 +    private List<ServiceImpl> services;
 +
++    @JsonProperty("applications")
++    private List<ApplicationImpl> applications;
++
 +    private String name = null;
 +
 +    void setName(String name) {
 +        this.name = name;
 +    }
 +
 +    @Override
 +    public String getName() {
 +        return name;
 +    }
 +
 +    @Override
 +    public String getDiscoveryType() {
 +        return discoveryType;
 +    }
 +
 +    @Override
 +    public String getDiscoveryAddress() {
 +        return discoveryAddress;
 +    }
 +
 +    @Override
 +    public String getDiscoveryUser() {
 +        return discoveryUser;
 +    }
 +
 +    @Override
 +    public String getDiscoveryPasswordAlias() {
 +        return discoveryPasswordAlias;
 +    }
 +
 +    @Override
 +    public String getClusterName() {
 +        return cluster;
 +    }
 +
 +    @Override
 +    public String getProviderConfig() {
 +        return providerConfig;
 +    }
 +
 +    @Override
 +    public List<Service> getServices() {
 +        List<Service> result = new ArrayList<>();
-         result.addAll(services);
++        if (services != null) {
++            result.addAll(services);
++        }
++        return result;
++    }
++
++    @Override
++    public List<Application> getApplications() {
++        List<Application> result = new ArrayList<>();
++        if (applications != null) {
++            result.addAll(applications);
++        }
 +        return result;
 +    }
 +
 +    public static class ServiceImpl implements Service {
 +        @JsonProperty("name")
 +        private String name;
 +
 +        @JsonProperty("params")
 +        private Map<String, String> params;
 +
 +        @JsonProperty("urls")
 +        private List<String> urls;
 +
 +        @Override
 +        public String getName() {
 +            return name;
 +        }
 +
 +        @Override
 +        public Map<String, String> getParams() {
 +            return params;
 +        }
 +
 +        @Override
 +        public List<String> getURLs() {
 +            return urls;
 +        }
 +    }
 +
++    public static class ApplicationImpl implements Application {
++        @JsonProperty("name")
++        private String name;
++
++        @JsonProperty("params")
++        private Map<String, String> params;
++
++        @JsonProperty("urls")
++        private List<String> urls;
++
++        @Override
++        public String getName() {
++            return name;
++        }
++
++        @Override
++        public Map<String, String> getParams() {
++            return params;
++        }
++
++        @Override
++        public List<String> getURLs() {
++            return urls;
++        }
++    }
++
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/main/java/org/apache/knox/gateway/topology/xml/KnoxFormatXmlTopologyRules.java
----------------------------------------------------------------------
diff --cc 
gateway-server/src/main/java/org/apache/knox/gateway/topology/xml/KnoxFormatXmlTopologyRules.java
index 81aedec,0000000..a1fcb6d
mode 100644,000000..100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/topology/xml/KnoxFormatXmlTopologyRules.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/topology/xml/KnoxFormatXmlTopologyRules.java
@@@ -1,95 -1,0 +1,97 @@@
 +/**
 + * 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.xml;
 +
 +import org.apache.commons.digester3.Rule;
 +import org.apache.commons.digester3.binder.AbstractRulesModule;
 +import org.apache.knox.gateway.topology.Application;
 +import org.apache.knox.gateway.topology.Param;
 +import org.apache.knox.gateway.topology.Provider;
 +import org.apache.knox.gateway.topology.Service;
 +import org.apache.knox.gateway.topology.Version;
 +import org.apache.knox.gateway.topology.builder.BeanPropertyTopologyBuilder;
 +import org.xml.sax.Attributes;
 +
 +public class KnoxFormatXmlTopologyRules extends AbstractRulesModule {
 +
 +  private static final String ROOT_TAG = "topology";
 +  private static final String NAME_TAG = "name";
 +  private static final String VERSION_TAG = "version";
 +  private static final String DEFAULT_SERVICE_TAG = "path";
++  private static final String GENERATED_TAG = "generated";
 +  private static final String APPLICATION_TAG = "application";
 +  private static final String SERVICE_TAG = "service";
 +  private static final String ROLE_TAG = "role";
 +  private static final String URL_TAG = "url";
 +  private static final String PROVIDER_TAG = "gateway/provider";
 +  private static final String ENABLED_TAG = "enabled";
 +  private static final String PARAM_TAG = "param";
 +  private static final String VALUE_TAG = "value";
 +
 +  private static final Rule paramRule = new ParamRule();
 +
 +  @Override
 +  protected void configure() {
 +    forPattern( ROOT_TAG ).createObject().ofType( 
BeanPropertyTopologyBuilder.class );
 +    forPattern( ROOT_TAG + "/" + NAME_TAG 
).callMethod("name").usingElementBodyAsArgument();
 +    forPattern( ROOT_TAG + "/" + VERSION_TAG 
).callMethod("version").usingElementBodyAsArgument();
 +    forPattern( ROOT_TAG + "/" + DEFAULT_SERVICE_TAG 
).callMethod("defaultService").usingElementBodyAsArgument();
++    forPattern( ROOT_TAG + "/" + GENERATED_TAG 
).callMethod("generated").usingElementBodyAsArgument();
 +
 +    forPattern( ROOT_TAG + "/" + APPLICATION_TAG ).createObject().ofType( 
Application.class ).then().setNext( "addApplication" );
 +    forPattern( ROOT_TAG + "/" + APPLICATION_TAG + "/" + ROLE_TAG 
).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + APPLICATION_TAG + "/" + NAME_TAG 
).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + APPLICATION_TAG + "/" + VERSION_TAG 
).createObject().ofType(Version.class).then().setBeanProperty().then().setNext("setVersion");
 +    forPattern( ROOT_TAG + "/" + APPLICATION_TAG + "/" + URL_TAG 
).callMethod( "addUrl" ).usingElementBodyAsArgument();
 +    forPattern( ROOT_TAG + "/" + APPLICATION_TAG + "/" + PARAM_TAG 
).createObject().ofType( Param.class ).then().addRule( paramRule 
).then().setNext( "addParam" );
 +    forPattern( ROOT_TAG + "/" + APPLICATION_TAG + "/" + PARAM_TAG + "/" + 
NAME_TAG ).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + APPLICATION_TAG + "/" + PARAM_TAG + "/" + 
VALUE_TAG ).setBeanProperty();
 +
 +    forPattern( ROOT_TAG + "/" + SERVICE_TAG ).createObject().ofType( 
Service.class ).then().setNext( "addService" );
 +    forPattern( ROOT_TAG + "/" + SERVICE_TAG + "/" + ROLE_TAG 
).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + SERVICE_TAG + "/" + NAME_TAG 
).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + SERVICE_TAG + "/" + VERSION_TAG 
).createObject().ofType(Version.class).then().setBeanProperty().then().setNext("setVersion");
 +    forPattern( ROOT_TAG + "/" + SERVICE_TAG + "/" + URL_TAG ).callMethod( 
"addUrl" ).usingElementBodyAsArgument();
 +    forPattern( ROOT_TAG + "/" + SERVICE_TAG + "/" + PARAM_TAG 
).createObject().ofType( Param.class ).then().addRule( paramRule 
).then().setNext( "addParam" );
 +    forPattern( ROOT_TAG + "/" + SERVICE_TAG + "/" + PARAM_TAG + "/" + 
NAME_TAG ).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + SERVICE_TAG + "/" + PARAM_TAG + "/" + 
VALUE_TAG ).setBeanProperty();
 +
 +    forPattern( ROOT_TAG + "/" + PROVIDER_TAG ).createObject().ofType( 
Provider.class ).then().setNext( "addProvider" );
 +    forPattern( ROOT_TAG + "/" + PROVIDER_TAG + "/" + ROLE_TAG 
).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + PROVIDER_TAG + "/" + ENABLED_TAG 
).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + PROVIDER_TAG + "/" + NAME_TAG 
).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + PROVIDER_TAG + "/" + PARAM_TAG 
).createObject().ofType( Param.class ).then().addRule( paramRule 
).then().setNext( "addParam" );
 +    forPattern( ROOT_TAG + "/" + PROVIDER_TAG + "/" + PARAM_TAG + "/" + 
NAME_TAG ).setBeanProperty();
 +    forPattern( ROOT_TAG + "/" + PROVIDER_TAG + "/" + PARAM_TAG + "/" + 
VALUE_TAG ).setBeanProperty();
 +  }
 +
 +  private static class ParamRule extends Rule {
 +
 +    @Override
 +    public void begin( String namespace, String name, Attributes attributes ) 
{
 +      Param param = getDigester().peek();
 +      String paramName = attributes.getValue( "name" );
 +      if( paramName != null ) {
 +        param.setName( paramName );
 +        param.setValue( attributes.getValue( "value" ) );
 +      }
 +    }
 +
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorFactoryTest.java
----------------------------------------------------------------------
diff --cc 
gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorFactoryTest.java
index 41a7c10,0000000..df31f3d
mode 100644,000000..100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorFactoryTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorFactoryTest.java
@@@ -1,422 -1,0 +1,681 @@@
 +/**
 + * 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.simple;
 +
 +import java.io.File;
 +import java.io.FileWriter;
 +import java.io.Writer;
 +import java.util.*;
 +
 +import org.junit.Test;
 +import static org.junit.Assert.*;
 +
 +
 +public class SimpleDescriptorFactoryTest {
 +
++    private enum FileType {
++        JSON,
++        YAML
++    }
 +
 +    @Test
 +    public void testParseJSONSimpleDescriptor() throws Exception {
++        testParseSimpleDescriptor(FileType.JSON);
++    }
++
++    @Test
++    public void testParseYAMLSimpleDescriptor() throws Exception {
++        testParseSimpleDescriptor(FileType.YAML);
++    }
++
++    @Test
++    public void testParseJSONSimpleDescriptorWithServiceParams() throws 
Exception {
++        testParseSimpleDescriptorWithServiceParams(FileType.JSON);
++    }
++
++    @Test
++    public void testParseYAMLSimpleDescriptorWithServiceParams() throws 
Exception {
++        testParseSimpleDescriptorWithServiceParams(FileType.YAML);
++    }
++
++    @Test
++    public void testParseJSONSimpleDescriptorWithApplications() throws 
Exception {
++        testParseSimpleDescriptorWithApplications(FileType.JSON);
++    }
++
++    @Test
++    public void testParseYAMLSimpleDescriptorApplications() throws Exception {
++        testParseSimpleDescriptorWithApplications(FileType.YAML);
++    }
++
++
++    @Test
++    public void testParseJSONSimpleDescriptorWithServicesAndApplications() 
throws Exception {
++        testParseSimpleDescriptorWithServicesAndApplications(FileType.JSON);
++    }
++
++    @Test
++    public void testParseYAMLSimpleDescriptorWithServicesAndApplications() 
throws Exception {
++        testParseSimpleDescriptorWithServicesAndApplications(FileType.YAML);
++    }
++
 +
++    private void testParseSimpleDescriptor(FileType type) throws Exception {
 +        final String   discoveryType    = "AMBARI";
 +        final String   discoveryAddress = 
"http://c6401.ambari.apache.org:8080";;
-         final String   discoveryUser    = "admin";
++        final String   discoveryUser    = "joeblow";
 +        final String   providerConfig   = "ambari-cluster-policy.xml";
 +        final String   clusterName      = "myCluster";
 +
 +        final Map<String, List<String>> services = new HashMap<>();
 +        services.put("NODEMANAGER", null);
 +        services.put("JOBTRACKER", null);
 +        services.put("RESOURCEMANAGER", null);
 +        services.put("HIVE", Arrays.asList("http://c6401.ambari.apache.org";, 
"http://c6402.ambari.apache.org";, "http://c6403.ambari.apache.org";));
-         services.put("AMBARIUI", 
Arrays.asList("http://c6401.ambari.apache.org:8080";));
++        services.put("AMBARIUI", 
Collections.singletonList("http://c6401.ambari.apache.org:8080";));
 +
-         String fileName = "test-topology.json";
-         File testJSON = null;
++        String fileName = "test-topology." + getFileExtensionForType(type);
++        File testFile = null;
 +        try {
-             testJSON = writeJSON(fileName, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services);
-             SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testJSON.getAbsolutePath());
++            testFile = writeDescriptorFile(type,
++                                           fileName,
++                                           discoveryType,
++                                           discoveryAddress,
++                                           discoveryUser,
++                                           providerConfig,
++                                           clusterName,
++                                           services);
++            SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testFile.getAbsolutePath());
 +            validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, services);
 +        } catch (Exception e) {
 +            e.printStackTrace();
 +        } finally {
-             if (testJSON != null) {
++            if (testFile != null) {
 +                try {
-                     testJSON.delete();
++                    testFile.delete();
 +                } catch (Exception e) {
 +                    // Ignore
 +                }
 +            }
 +        }
 +    }
 +
-     @Test
-     public void testParseJSONSimpleDescriptorWithServiceParams() throws 
Exception {
++    private void testParseSimpleDescriptorWithServiceParams(FileType type) 
throws Exception {
 +
 +        final String   discoveryType    = "AMBARI";
 +        final String   discoveryAddress = 
"http://c6401.ambari.apache.org:8080";;
 +        final String   discoveryUser    = "admin";
 +        final String   providerConfig   = "ambari-cluster-policy.xml";
 +        final String   clusterName      = "myCluster";
 +
 +        final Map<String, List<String>> services = new HashMap<>();
 +        services.put("NODEMANAGER", null);
 +        services.put("JOBTRACKER", null);
 +        services.put("RESOURCEMANAGER", null);
 +        services.put("HIVE", Arrays.asList("http://c6401.ambari.apache.org";, 
"http://c6402.ambari.apache.org";, "http://c6403.ambari.apache.org";));
 +        services.put("AMBARIUI", 
Collections.singletonList("http://c6401.ambari.apache.org:8080";));
 +        services.put("KNOXSSO", null);
 +        services.put("KNOXTOKEN", null);
 +        services.put("CustomRole", 
Collections.singletonList("http://c6402.ambari.apache.org:1234";));
 +
 +        final Map<String, Map<String, String>> serviceParams = new 
HashMap<>();
 +        Map<String, String> knoxSSOParams = new HashMap<>();
 +        knoxSSOParams.put("knoxsso.cookie.secure.only", "true");
 +        knoxSSOParams.put("knoxsso.token.ttl", "100000");
 +        serviceParams.put("KNOXSSO", knoxSSOParams);
 +
 +        Map<String, String> knoxTokenParams = new HashMap<>();
 +        knoxTokenParams.put("knox.token.ttl", "36000000");
 +        knoxTokenParams.put("knox.token.audiences", "tokenbased");
 +        knoxTokenParams.put("knox.token.target.url", 
"https://localhost:8443/gateway/tokenbased";);
 +        serviceParams.put("KNOXTOKEN", knoxTokenParams);
 +
 +        Map<String, String> customRoleParams = new HashMap<>();
 +        customRoleParams.put("custom.param.1", "value1");
 +        customRoleParams.put("custom.param.2", "value2");
 +        serviceParams.put("CustomRole", customRoleParams);
 +
-         String fileName = "test-topology.json";
-         File testJSON = null;
++        String fileName = "test-topology." + getFileExtensionForType(type);
++        File testFile = null;
 +        try {
-             testJSON = writeJSON(fileName,
-                                  discoveryType,
-                                  discoveryAddress,
-                                  discoveryUser,
-                                  providerConfig,
-                                  clusterName,
-                                  services,
-                                  serviceParams);
-             SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testJSON.getAbsolutePath());
++            testFile = writeDescriptorFile(type,
++                                           fileName,
++                                           discoveryType,
++                                           discoveryAddress,
++                                           discoveryUser,
++                                           providerConfig,
++                                           clusterName,
++                                           services,
++                                           serviceParams);
++            SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testFile.getAbsolutePath());
 +            validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, services, serviceParams);
-         } catch (Exception e) {
-             e.printStackTrace();
 +        } finally {
-             if (testJSON != null) {
++            if (testFile != null) {
 +                try {
-                     testJSON.delete();
++                    testFile.delete();
 +                } catch (Exception e) {
 +                    // Ignore
 +                }
 +            }
 +        }
 +    }
 +
-     @Test
-     public void testParseYAMLSimpleDescriptor() throws Exception {
++    private void testParseSimpleDescriptorWithApplications(FileType type) 
throws Exception {
 +
 +        final String   discoveryType    = "AMBARI";
 +        final String   discoveryAddress = 
"http://c6401.ambari.apache.org:8080";;
-         final String   discoveryUser    = "joeblow";
++        final String   discoveryUser    = "admin";
 +        final String   providerConfig   = "ambari-cluster-policy.xml";
 +        final String   clusterName      = "myCluster";
 +
-         final Map<String, List<String>> services = new HashMap<>();
-         services.put("NODEMANAGER", null);
-         services.put("JOBTRACKER", null);
-         services.put("RESOURCEMANAGER", null);
-         services.put("HIVE", Arrays.asList("http://c6401.ambari.apache.org";, 
"http://c6402.ambari.apache.org";, "http://c6403.ambari.apache.org";));
-         services.put("AMBARIUI", 
Arrays.asList("http://c6401.ambari.apache.org:8080";));
- 
-         String fileName = "test-topology.yml";
-         File testYAML = null;
++        final Map<String, List<String>> apps = new HashMap<>();
++        apps.put("app-one", null);
++        apps.put("appTwo", null);
++        apps.put("thirdApps", null);
++        apps.put("appfour", Arrays.asList("http://host1:1234";, 
"http://host2:5678";, "http://host1:1357";));
++        apps.put("AppFive", Collections.singletonList("http://host5:8080";));
++
++        final Map<String, Map<String, String>> appParams = new HashMap<>();
++        Map<String, String> oneParams = new HashMap<>();
++        oneParams.put("appone.cookie.secure.only", "true");
++        oneParams.put("appone.token.ttl", "100000");
++        appParams.put("app-one", oneParams);
++        Map<String, String> fiveParams = new HashMap<>();
++        fiveParams.put("myproperty", "true");
++        fiveParams.put("anotherparam", "100000");
++        appParams.put("AppFive", fiveParams);
++
++        String fileName = "test-topology." + getFileExtensionForType(type);
++        File testFile = null;
 +        try {
-             testYAML = writeYAML(fileName, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services);
-             SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testYAML.getAbsolutePath());
-             validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, services);
-         } catch (Exception e) {
-             e.printStackTrace();
++            testFile = writeDescriptorFile(type,
++                                           fileName,
++                                           discoveryType,
++                                           discoveryAddress,
++                                           discoveryUser,
++                                           providerConfig,
++                                           clusterName,
++                                           null,
++                                           null,
++                                           apps,
++                                           appParams);
++            SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testFile.getAbsolutePath());
++            validateSimpleDescriptor(sd,
++                                     discoveryType,
++                                     discoveryAddress,
++                                     providerConfig,
++                                     clusterName,
++                                     null,
++                                     null,
++                                     apps,
++                                     appParams);
 +        } finally {
-             if (testYAML != null) {
++            if (testFile != null) {
 +                try {
-                     testYAML.delete();
++                    testFile.delete();
 +                } catch (Exception e) {
 +                    // Ignore
 +                }
 +            }
 +        }
 +    }
 +
- 
-     @Test
-     public void testParseYAMLSimpleDescriptorWithServiceParams() throws 
Exception {
++    private void 
testParseSimpleDescriptorWithServicesAndApplications(FileType type) throws 
Exception {
 +
 +        final String   discoveryType    = "AMBARI";
 +        final String   discoveryAddress = 
"http://c6401.ambari.apache.org:8080";;
-         final String   discoveryUser    = "joeblow";
++        final String   discoveryUser    = "admin";
 +        final String   providerConfig   = "ambari-cluster-policy.xml";
 +        final String   clusterName      = "myCluster";
 +
 +        final Map<String, List<String>> services = new HashMap<>();
 +        services.put("NODEMANAGER", null);
 +        services.put("JOBTRACKER", null);
 +        services.put("RESOURCEMANAGER", null);
 +        services.put("HIVE", Arrays.asList("http://c6401.ambari.apache.org";, 
"http://c6402.ambari.apache.org";, "http://c6403.ambari.apache.org";));
-         services.put("AMBARIUI", 
Arrays.asList("http://c6401.ambari.apache.org:8080";));
++        services.put("AMBARIUI", 
Collections.singletonList("http://c6401.ambari.apache.org:8080";));
 +        services.put("KNOXSSO", null);
 +        services.put("KNOXTOKEN", null);
 +        services.put("CustomRole", 
Collections.singletonList("http://c6402.ambari.apache.org:1234";));
 +
 +        final Map<String, Map<String, String>> serviceParams = new 
HashMap<>();
 +        Map<String, String> knoxSSOParams = new HashMap<>();
 +        knoxSSOParams.put("knoxsso.cookie.secure.only", "true");
 +        knoxSSOParams.put("knoxsso.token.ttl", "100000");
 +        serviceParams.put("KNOXSSO", knoxSSOParams);
 +
 +        Map<String, String> knoxTokenParams = new HashMap<>();
 +        knoxTokenParams.put("knox.token.ttl", "36000000");
 +        knoxTokenParams.put("knox.token.audiences", "tokenbased");
 +        knoxTokenParams.put("knox.token.target.url", 
"https://localhost:8443/gateway/tokenbased";);
 +        serviceParams.put("KNOXTOKEN", knoxTokenParams);
 +
 +        Map<String, String> customRoleParams = new HashMap<>();
 +        customRoleParams.put("custom.param.1", "value1");
 +        customRoleParams.put("custom.param.2", "value2");
 +        serviceParams.put("CustomRole", customRoleParams);
 +
-         String fileName = "test-topology.yml";
-         File testYAML = null;
++        final Map<String, List<String>> apps = new HashMap<>();
++        apps.put("app-one", null);
++        apps.put("appTwo", null);
++        apps.put("thirdApps", null);
++        apps.put("appfour", Arrays.asList("http://host1:1234";, 
"http://host2:5678";, "http://host1:1357";));
++        apps.put("AppFive", Collections.singletonList("http://host5:8080";));
++
++        final Map<String, Map<String, String>> appParams = new HashMap<>();
++        Map<String, String> oneParams = new HashMap<>();
++        oneParams.put("appone.cookie.secure.only", "true");
++        oneParams.put("appone.token.ttl", "100000");
++        appParams.put("app-one", oneParams);
++        Map<String, String> fiveParams = new HashMap<>();
++        fiveParams.put("myproperty", "true");
++        fiveParams.put("anotherparam", "100000");
++        appParams.put("AppFive", fiveParams);
++
++        String fileName = "test-topology." + getFileExtensionForType(type);
++        File testFile = null;
 +        try {
-             testYAML = writeYAML(fileName, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services, serviceParams);
-             SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testYAML.getAbsolutePath());
-             validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, services, serviceParams);
-         } catch (Exception e) {
-             e.printStackTrace();
++            testFile = writeDescriptorFile(type,
++                                           fileName,
++                                           discoveryType,
++                                           discoveryAddress,
++                                           discoveryUser,
++                                           providerConfig,
++                                           clusterName,
++                                           services,
++                                           serviceParams,
++                                           apps,
++                                           appParams);
++            SimpleDescriptor sd = 
SimpleDescriptorFactory.parse(testFile.getAbsolutePath());
++            validateSimpleDescriptor(sd,
++                                     discoveryType,
++                                     discoveryAddress,
++                                     providerConfig,
++                                     clusterName,
++                                     services,
++                                     serviceParams,
++                                     apps,
++                                     appParams);
 +        } finally {
-             if (testYAML != null) {
++            if (testFile != null) {
 +                try {
-                     testYAML.delete();
++                    testFile.delete();
 +                } catch (Exception e) {
 +                    // Ignore
 +                }
 +            }
 +        }
 +    }
 +
- 
-     private void validateSimpleDescriptor(SimpleDescriptor          sd,
-                                           String                    
discoveryType,
-                                           String                    
discoveryAddress,
-                                           String                    
providerConfig,
-                                           String                    
clusterName,
-                                           Map<String, List<String>> 
expectedServices) {
-         validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, expectedServices, null);
-     }
- 
- 
-     private void validateSimpleDescriptor(SimpleDescriptor                 sd,
-                                           String                           
discoveryType,
-                                           String                           
discoveryAddress,
-                                           String                           
providerConfig,
-                                           String                           
clusterName,
-                                           Map<String, List<String>>        
expectedServices,
-                                           Map<String, Map<String, String>> 
expectedServiceParameters) {
-         assertNotNull(sd);
-         assertEquals(discoveryType, sd.getDiscoveryType());
-         assertEquals(discoveryAddress, sd.getDiscoveryAddress());
-         assertEquals(providerConfig, sd.getProviderConfig());
-         assertEquals(clusterName, sd.getClusterName());
- 
-         List<SimpleDescriptor.Service> actualServices = sd.getServices();
- 
-         assertEquals(expectedServices.size(), actualServices.size());
- 
-         for (SimpleDescriptor.Service actualService : actualServices) {
-             assertTrue(expectedServices.containsKey(actualService.getName()));
-             assertEquals(expectedServices.get(actualService.getName()), 
actualService.getURLs());
- 
-             // Validate service parameters
-             if (expectedServiceParameters != null) {
-                 if 
(expectedServiceParameters.containsKey(actualService.getName())) {
-                     Map<String, String> expectedParams = 
expectedServiceParameters.get(actualService.getName());
- 
-                     Map<String, String> actualServiceParams = 
actualService.getParams();
-                     assertNotNull(actualServiceParams);
- 
-                     // Validate the size of the service parameter set
-                     assertEquals(expectedParams.size(), 
actualServiceParams.size());
- 
-                     // Validate the parameter contents
-                     for (String paramName : actualServiceParams.keySet()) {
-                         assertTrue(expectedParams.containsKey(paramName));
-                         assertEquals(expectedParams.get(paramName), 
actualServiceParams.get(paramName));
-                     }
-                 }
-             }
++    private String getFileExtensionForType(FileType type) {
++        String extension = null;
++        switch (type) {
++            case JSON:
++                extension = "json";
++                break;
++            case YAML:
++                extension = "yml";
++                break;
 +        }
++        return extension;
 +    }
 +
++    private File writeDescriptorFile(FileType type,
++                                     String                           path,
++                                     String                           
discoveryType,
++                                     String                           
discoveryAddress,
++                                     String                           
discoveryUser,
++                                     String                           
providerConfig,
++                                     String                           
clusterName,
++                                     Map<String, List<String>>        
services) throws Exception {
++        return writeDescriptorFile(type,
++                                   path,
++                                   discoveryType,
++                                   discoveryAddress,
++                                   discoveryUser,
++                                   providerConfig,
++                                   clusterName,
++                                   services,
++                                   null);
++    }
 +
-     private File writeJSON(String path, String content) throws Exception {
-         File f = new File(path);
- 
-         Writer fw = new FileWriter(f);
-         fw.write(content);
-         fw.flush();
-         fw.close();
- 
-         return f;
++    private File writeDescriptorFile(FileType type,
++                                     String                           path,
++                                     String                           
discoveryType,
++                                     String                           
discoveryAddress,
++                                     String                           
discoveryUser,
++                                     String                           
providerConfig,
++                                     String                           
clusterName,
++                                     Map<String, List<String>>        
services,
++                                     Map<String, Map<String, String>> 
serviceParams) throws Exception {
++        return writeDescriptorFile(type,
++                                   path,
++                                   discoveryType,
++                                   discoveryAddress,
++                                   discoveryUser,
++                                   providerConfig,
++                                   clusterName,
++                                   services,
++                                   serviceParams,
++                                   null,
++                                   null);
 +    }
 +
 +
-     private File writeJSON(String path,
-                            String discoveryType,
-                            String discoveryAddress,
-                            String discoveryUser,
-                            String providerConfig,
-                            String clusterName,
-                            Map<String, List<String>> services) throws 
Exception {
-         return writeJSON(path, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services, null);
++    private File writeDescriptorFile(FileType type,
++                                     String                           path,
++                                     String                           
discoveryType,
++                                     String                           
discoveryAddress,
++                                     String                           
discoveryUser,
++                                     String                           
providerConfig,
++                                     String                           
clusterName,
++                                     Map<String, List<String>>        
services,
++                                     Map<String, Map<String, String>> 
serviceParams,
++                                     Map<String, List<String>>        apps,
++                                     Map<String, Map<String, String>> 
appParams) throws Exception {
++        File result = null;
++        switch (type) {
++            case JSON:
++                result = writeJSON(path,
++                                   discoveryType,
++                                   discoveryAddress,
++                                   discoveryUser,
++                                   providerConfig,
++                                   clusterName,
++                                   services,
++                                   serviceParams,
++                                   apps,
++                                   appParams);
++                break;
++            case YAML:
++                result = writeYAML(path,
++                                   discoveryType,
++                                   discoveryAddress,
++                                   discoveryUser,
++                                   providerConfig,
++                                   clusterName,
++                                   services,
++                                   serviceParams,
++                                   apps,
++                                   appParams);
++                break;
++        }
++        return result;
 +    }
 +
++
 +    private File writeJSON(String path,
 +                           String discoveryType,
 +                           String discoveryAddress,
 +                           String discoveryUser,
 +                           String providerConfig,
 +                           String clusterName,
 +                           Map<String, List<String>> services,
-                            Map<String, Map<String, String>> serviceParams) 
throws Exception {
++                           Map<String, Map<String, String>> serviceParams,
++                           Map<String, List<String>> apps,
++                           Map<String, Map<String, String>> appParams) throws 
Exception {
 +        File f = new File(path);
 +
 +        Writer fw = new FileWriter(f);
 +        fw.write("{" + "\n");
 +        fw.write("\"discovery-type\":\"" + discoveryType + "\",\n");
 +        fw.write("\"discovery-address\":\"" + discoveryAddress + "\",\n");
 +        fw.write("\"discovery-user\":\"" + discoveryUser + "\",\n");
 +        fw.write("\"provider-config-ref\":\"" + providerConfig + "\",\n");
-         fw.write("\"cluster\":\"" + clusterName + "\",\n");
-         fw.write("\"services\":[\n");
- 
-         int i = 0;
-         for (String name : services.keySet()) {
-             fw.write("{\"name\":\"" + name + "\"");
- 
-             // Service params
-             if (serviceParams != null && !serviceParams.isEmpty()) {
-                 Map<String, String> params = serviceParams.get(name);
-                 if (params != null && !params.isEmpty()) {
-                     fw.write(",\n\"params\":{\n");
-                     Iterator<String> paramNames = params.keySet().iterator();
-                     while (paramNames.hasNext()) {
-                         String paramName = paramNames.next();
-                         String paramValue = params.get(paramName);
-                         fw.write("\"" + paramName + "\":\"" + paramValue + 
"\"");
-                         fw.write(paramNames.hasNext() ? ",\n" : "");
-                     }
-                     fw.write("\n}");
-                 }
-             }
++        fw.write("\"cluster\":\"" + clusterName + "\"");
 +
-             // Service URLs
-             List<String> urls = services.get(name);
-             if (urls != null) {
-                 fw.write(",\n\"urls\":[");
-                 Iterator<String> urlIter = urls.iterator();
-                 while (urlIter.hasNext()) {
-                     fw.write("\"" + urlIter.next() + "\"");
-                     if (urlIter.hasNext()) {
-                         fw.write(", ");
-                     }
-                 }
-                 fw.write("]\n");
-             }
++        if (services != null && !services.isEmpty()) {
++            fw.write(",\n\"services\":[\n");
++            writeServiceOrApplicationJSON(fw, services, serviceParams);
++            fw.write("]\n");
++        }
 +
-             fw.write("}");
-             if (i++ < services.size() - 1) {
-                 fw.write(",");
-             }
-             fw.write("\n");
++        if (apps != null && !apps.isEmpty()) {
++            fw.write(",\n\"applications\":[\n");
++            writeServiceOrApplicationJSON(fw, apps, appParams);
++            fw.write("]\n");
 +        }
-         fw.write("]\n");
++
 +        fw.write("}\n");
 +        fw.flush();
 +        fw.close();
 +
 +        return f;
 +    }
 +
++    private void writeServiceOrApplicationJSON(Writer fw,
++                                               Map<String, List<String>> 
elementURLs,
++                                               Map<String, Map<String, 
String>> elementParams) throws Exception {
++        if (elementURLs != null) {
++            int i = 0;
++            for (String name : elementURLs.keySet()) {
++                fw.write("{\"name\":\"" + name + "\"");
++
++                // Service params
++                if (elementParams != null && !elementParams.isEmpty()) {
++                    Map<String, String> params = elementParams.get(name);
++                    if (params != null && !params.isEmpty()) {
++                        fw.write(",\n\"params\":{\n");
++                        Iterator<String> paramNames = 
params.keySet().iterator();
++                        while (paramNames.hasNext()) {
++                            String paramName = paramNames.next();
++                            String paramValue = params.get(paramName);
++                            fw.write("\"" + paramName + "\":\"" + paramValue 
+ "\"");
++                            fw.write(paramNames.hasNext() ? ",\n" : "");
++                        }
++                        fw.write("\n}");
++                    }
++                }
 +
-     private File writeYAML(String                    path,
-                            String                    discoveryType,
-                            String                    discoveryAddress,
-                            String                    discoveryUser,
-                            String                    providerConfig,
-                            String                    clusterName,
-                            Map<String, List<String>> services) throws 
Exception {
-         return writeYAML(path, discoveryType, discoveryAddress, 
discoveryUser, providerConfig, clusterName, services, null);
-     }
++                // Service URLs
++                List<String> urls = elementURLs.get(name);
++                if (urls != null) {
++                    fw.write(",\n\"urls\":[");
++                    Iterator<String> urlIter = urls.iterator();
++                    while (urlIter.hasNext()) {
++                        fw.write("\"" + urlIter.next() + "\"");
++                        if (urlIter.hasNext()) {
++                            fw.write(", ");
++                        }
++                    }
++                    fw.write("]\n");
++                }
 +
++                fw.write("}");
++                if (i++ < elementURLs.size() - 1) {
++                    fw.write(",");
++                }
++                fw.write("\n");
++            }
++        }
++    }
 +
 +    private File writeYAML(String                           path,
 +                           String                           discoveryType,
 +                           String                           discoveryAddress,
 +                           String                           discoveryUser,
 +                           String                           providerConfig,
 +                           String                           clusterName,
 +                           Map<String, List<String>>        services,
-                            Map<String, Map<String, String>> serviceParams) 
throws Exception {
++                           Map<String, Map<String, String>> serviceParams,
++                           Map<String, List<String>>        apps,
++                           Map<String, Map<String, String>> appParams) throws 
Exception {
++
 +        File f = new File(path);
 +
 +        Writer fw = new FileWriter(f);
 +        fw.write("---" + "\n");
 +        fw.write("discovery-type: " + discoveryType + "\n");
 +        fw.write("discovery-address: " + discoveryAddress + "\n");
 +        fw.write("discovery-user: " + discoveryUser + "\n");
 +        fw.write("provider-config-ref: " + providerConfig + "\n");
 +        fw.write("cluster: " + clusterName+ "\n");
-         fw.write("services:\n");
-         for (String name : services.keySet()) {
++
++        if (services != null && !services.isEmpty()) {
++            fw.write("services:\n");
++            writeServiceOrApplicationYAML(fw, services, serviceParams);
++        }
++
++        if (apps != null && !apps.isEmpty()) {
++            fw.write("applications:\n");
++            writeServiceOrApplicationYAML(fw, apps, appParams);
++        }
++
++        fw.flush();
++        fw.close();
++
++        return f;
++    }
++
++    private void writeServiceOrApplicationYAML(Writer                         
  fw,
++                                               Map<String, List<String>>      
  elementURLs,
++                                               Map<String, Map<String, 
String>> elementParams) throws Exception {
++        for (String name : elementURLs.keySet()) {
 +            fw.write("    - name: " + name + "\n");
 +
 +            // Service params
-             if (serviceParams != null && !serviceParams.isEmpty()) {
-                 if (serviceParams.containsKey(name)) {
-                     Map<String, String> params = serviceParams.get(name);
++            if (elementParams != null && !elementParams.isEmpty()) {
++                if (elementParams.containsKey(name)) {
++                    Map<String, String> params = elementParams.get(name);
 +                    fw.write("      params:\n");
 +                    for (String paramName : params.keySet()) {
 +                        fw.write("            " + paramName + ": " + 
params.get(paramName) + "\n");
 +                    }
 +                }
 +            }
 +
 +            // Service URLs
-             List<String> urls = services.get(name);
++            List<String> urls = elementURLs.get(name);
 +            if (urls != null) {
 +                fw.write("      urls:\n");
 +                for (String url : urls) {
 +                    fw.write("          - " + url + "\n");
 +                }
 +            }
 +        }
-         fw.flush();
-         fw.close();
++    }
 +
-         return f;
++
++    private void validateSimpleDescriptor(SimpleDescriptor          sd,
++                                          String                    
discoveryType,
++                                          String                    
discoveryAddress,
++                                          String                    
providerConfig,
++                                          String                    
clusterName,
++                                          Map<String, List<String>> 
expectedServices) {
++        validateSimpleDescriptor(sd, discoveryType, discoveryAddress, 
providerConfig, clusterName, expectedServices, null);
++    }
++
++
++    private void validateSimpleDescriptor(SimpleDescriptor                 sd,
++                                          String                           
discoveryType,
++                                          String                           
discoveryAddress,
++                                          String                           
providerConfig,
++                                          String                           
clusterName,
++                                          Map<String, List<String>>        
expectedServices,
++                                          Map<String, Map<String, String>> 
expectedServiceParameters) {
++        validateSimpleDescriptor(sd,
++                                 discoveryType,
++                                 discoveryAddress,
++                                 providerConfig,
++                                 clusterName,
++                                 expectedServices,
++                                 expectedServiceParameters,
++                                 null,
++                                 null);
 +    }
 +
++    private void validateSimpleDescriptor(SimpleDescriptor                 sd,
++                                          String                           
discoveryType,
++                                          String                           
discoveryAddress,
++                                          String                           
providerConfig,
++                                          String                           
clusterName,
++                                          Map<String, List<String>>        
expectedServices,
++                                          Map<String, Map<String, String>> 
expectedServiceParameters,
++                                          Map<String, List<String>>        
expectedApps,
++                                          Map<String, Map<String, String>> 
expectedAppParameters) {
++        assertNotNull(sd);
++        assertEquals(discoveryType, sd.getDiscoveryType());
++        assertEquals(discoveryAddress, sd.getDiscoveryAddress());
++        assertEquals(providerConfig, sd.getProviderConfig());
++        assertEquals(clusterName, sd.getClusterName());
++
++        List<SimpleDescriptor.Service> actualServices = sd.getServices();
++
++        if (expectedServices == null) {
++            assertTrue(actualServices.isEmpty());
++        } else {
++            assertEquals(expectedServices.size(), actualServices.size());
++
++            for (SimpleDescriptor.Service actualService : actualServices) {
++                
assertTrue(expectedServices.containsKey(actualService.getName()));
++                assertEquals(expectedServices.get(actualService.getName()), 
actualService.getURLs());
++
++                // Validate service parameters
++                if (expectedServiceParameters != null) {
++                    if 
(expectedServiceParameters.containsKey(actualService.getName())) {
++                        Map<String, String> expectedParams = 
expectedServiceParameters.get(actualService.getName());
++
++                        Map<String, String> actualServiceParams = 
actualService.getParams();
++                        assertNotNull(actualServiceParams);
++
++                        // Validate the size of the service parameter set
++                        assertEquals(expectedParams.size(), 
actualServiceParams.size());
++
++                        // Validate the parameter contents
++                        for (String paramName : actualServiceParams.keySet()) 
{
++                            assertTrue(expectedParams.containsKey(paramName));
++                            assertEquals(expectedParams.get(paramName), 
actualServiceParams.get(paramName));
++                        }
++                    }
++                }
++            }
++        }
++
++        List<SimpleDescriptor.Application> actualApps = sd.getApplications();
++
++        if (expectedApps == null) {
++            assertTrue(actualApps.isEmpty());
++        } else {
++            assertEquals(expectedApps.size(), actualApps.size());
++
++            for (SimpleDescriptor.Application actualApp : actualApps) {
++                assertTrue(expectedApps.containsKey(actualApp.getName()));
++                assertEquals(expectedApps.get(actualApp.getName()), 
actualApp.getURLs());
++
++                // Validate service parameters
++                if (expectedServiceParameters != null) {
++                    if 
(expectedAppParameters.containsKey(actualApp.getName())) {
++                        Map<String, String> expectedParams = 
expectedAppParameters.get(actualApp.getName());
++
++                        Map<String, String> actualAppParams = 
actualApp.getParams();
++                        assertNotNull(actualAppParams);
++
++                        // Validate the size of the service parameter set
++                        assertEquals(expectedParams.size(), 
actualAppParams.size());
++
++                        // Validate the parameter contents
++                        for (String paramName : actualAppParams.keySet()) {
++                            assertTrue(expectedParams.containsKey(paramName));
++                            assertEquals(expectedParams.get(paramName), 
actualAppParams.get(paramName));
++                        }
++                    }
++                }
++            }
++        }
++    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
----------------------------------------------------------------------
diff --cc 
gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
index f40fad7,0000000..575b68a
mode 100644,000000..100644
--- 
a/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandlerTest.java
@@@ -1,447 -1,0 +1,455 @@@
 +/**
 + * 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.simple;
 +
 +import org.apache.knox.gateway.topology.validation.TopologyValidator;
 +import org.apache.knox.gateway.util.XmlUtils;
 +import java.io.ByteArrayInputStream;
 +import java.io.File;
 +import java.io.FileNotFoundException;
 +import java.io.FileOutputStream;
 +import java.io.IOException;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Properties;
 +
 +import javax.xml.xpath.XPath;
 +import javax.xml.xpath.XPathConstants;
 +import javax.xml.xpath.XPathFactory;
 +
 +import org.apache.commons.io.FileUtils;
 +import org.easymock.EasyMock;
 +import org.junit.Test;
 +import org.w3c.dom.Document;
 +import org.w3c.dom.Node;
 +import org.w3c.dom.NodeList;
 +import org.xml.sax.SAXException;
 +
++import static org.hamcrest.Matchers.hasXPath;
++import static org.hamcrest.Matchers.is;
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotNull;
++import static org.junit.Assert.assertThat;
 +import static org.junit.Assert.assertTrue;
 +import static org.junit.Assert.fail;
 +
 +
 +public class SimpleDescriptorHandlerTest {
 +
 +    private static final String TEST_PROVIDER_CONFIG =
 +            "    <gateway>\n" +
 +                    "        <provider>\n" +
 +                    "            <role>authentication</role>\n" +
 +                    "            <name>ShiroProvider</name>\n" +
 +                    "            <enabled>true</enabled>\n" +
 +                    "            <param>\n" +
 +                    "                <!-- \n" +
 +                    "                session timeout in minutes,  this is 
really idle timeout,\n" +
 +                    "                defaults to 30mins, if the property 
value is not defined,, \n" +
 +                    "                current client authentication would 
expire if client idles contiuosly for more than this value\n" +
 +                    "                -->\n" +
 +                    "                <name>sessionTimeout</name>\n" +
 +                    "                <value>30</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                <name>main.ldapRealm</name>\n" +
 +                    "                
<value>org.apache.knox.gateway.shirorealm.KnoxLdapRealm</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                <name>main.ldapContextFactory</name>\n" +
 +                    "                
<value>org.apache.knox.gateway.shirorealm.KnoxLdapContextFactory</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                
<name>main.ldapRealm.contextFactory</name>\n" +
 +                    "                <value>$ldapContextFactory</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                
<name>main.ldapRealm.userDnTemplate</name>\n" +
 +                    "                
<value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                
<name>main.ldapRealm.contextFactory.url</name>\n" +
 +                    "                <value>ldap://localhost:33389</value>\n" 
+
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                
<name>main.ldapRealm.contextFactory.authenticationMechanism</name>\n" +
 +                    "                <value>simple</value>\n" +
 +                    "            </param>\n" +
 +                    "            <param>\n" +
 +                    "                <name>urls./**</name>\n" +
 +                    "                <value>authcBasic</value>\n" +
 +                    "            </param>\n" +
 +                    "        </provider>\n" +
 +                    "\n" +
 +                    "        <provider>\n" +
 +                    "            <role>identity-assertion</role>\n" +
 +                    "            <name>Default</name>\n" +
 +                    "            <enabled>true</enabled>\n" +
 +                    "        </provider>\n" +
 +                    "\n" +
 +                    "        <!--\n" +
 +                    "        Defines rules for mapping host names internal to 
a Hadoop cluster to externally accessible host names.\n" +
 +                    "        For example, a hadoop service running in AWS may 
return a response that includes URLs containing the\n" +
 +                    "        some AWS internal host name.  If the client 
needs to make a subsequent request to the host identified\n" +
 +                    "        in those URLs they need to be mapped to external 
host names that the client Knox can use to connect.\n" +
 +                    "\n" +
 +                    "        If the external hostname and internal host names 
are same turn of this provider by setting the value of\n" +
 +                    "        enabled parameter as false.\n" +
 +                    "\n" +
 +                    "        The name parameter specifies the external host 
names in a comma separated list.\n" +
 +                    "        The value parameter specifies corresponding 
internal host names in a comma separated list.\n" +
 +                    "\n" +
 +                    "        Note that when you are using Sandbox, the 
external hostname needs to be localhost, as seen in out\n" +
 +                    "        of box sandbox.xml.  This is because Sandbox 
uses port mapping to allow clients to connect to the\n" +
 +                    "        Hadoop services using localhost.  In real 
clusters, external host names would almost never be localhost.\n" +
 +                    "        -->\n" +
 +                    "        <provider>\n" +
 +                    "            <role>hostmap</role>\n" +
 +                    "            <name>static</name>\n" +
 +                    "            <enabled>true</enabled>\n" +
 +                    "            
<param><name>localhost</name><value>sandbox,sandbox.hortonworks.com</value></param>\n"
 +
 +                    "        </provider>\n" +
 +                    "    </gateway>\n";
 +
 +
 +    /**
 +     * KNOX-1006
 +     *
 +     * N.B. This test depends on the PropertiesFileServiceDiscovery extension 
being configured:
 +     *             
org.apache.knox.gateway.topology.discovery.test.extension.PropertiesFileServiceDiscovery
 +     */
 +    @Test
 +    public void testSimpleDescriptorHandler() throws Exception {
 +
 +        final String type = "PROPERTIES_FILE";
 +        final String clusterName = "dummy";
 +
 +        // Create a properties file to be the source of service discovery 
details for this test
 +        final File discoveryConfig = File.createTempFile(getClass().getName() 
+ "_discovery-config", ".properties");
 +
 +        final String address = discoveryConfig.getAbsolutePath();
 +
 +        final Properties DISCOVERY_PROPERTIES = new Properties();
 +        DISCOVERY_PROPERTIES.setProperty(clusterName + ".name", clusterName);
 +        DISCOVERY_PROPERTIES.setProperty(clusterName + ".NAMENODE", 
"hdfs://namenodehost:8020");
 +        DISCOVERY_PROPERTIES.setProperty(clusterName + ".JOBTRACKER", 
"rpc://jobtrackerhostname:8050");
 +        DISCOVERY_PROPERTIES.setProperty(clusterName + ".WEBHDFS", 
"http://webhdfshost:1234";);
 +        DISCOVERY_PROPERTIES.setProperty(clusterName + ".WEBHCAT", 
"http://webhcathost:50111/templeton";);
 +        DISCOVERY_PROPERTIES.setProperty(clusterName + ".OOZIE", 
"http://ooziehost:11000/oozie";);
 +        DISCOVERY_PROPERTIES.setProperty(clusterName + ".WEBHBASE", 
"http://webhbasehost:1234";);
 +        DISCOVERY_PROPERTIES.setProperty(clusterName + ".HIVE", 
"http://hivehostname:10001/clipath";);
 +        DISCOVERY_PROPERTIES.setProperty(clusterName + ".RESOURCEMANAGER", 
"http://remanhost:8088/ws";);
 +
 +        try {
 +            DISCOVERY_PROPERTIES.store(new FileOutputStream(discoveryConfig), 
null);
 +        } catch (FileNotFoundException e) {
 +            fail(e.getMessage());
 +        }
 +
 +        final Map<String, List<String>> serviceURLs = new HashMap<>();
 +        serviceURLs.put("NAMENODE", null);
 +        serviceURLs.put("JOBTRACKER", null);
 +        serviceURLs.put("WEBHDFS", null);
 +        serviceURLs.put("WEBHCAT", null);
 +        serviceURLs.put("OOZIE", null);
 +        serviceURLs.put("WEBHBASE", null);
 +        serviceURLs.put("HIVE", null);
 +        serviceURLs.put("RESOURCEMANAGER", null);
 +        serviceURLs.put("AMBARIUI", 
Collections.singletonList("http://c6401.ambari.apache.org:8080";));
 +        serviceURLs.put("KNOXSSO", null);
 +
 +        // Write the externalized provider config to a temp file
 +        File providerConfig = new File(System.getProperty("java.io.tmpdir"), 
"ambari-cluster-policy.xml");
 +        FileUtils.write(providerConfig, TEST_PROVIDER_CONFIG);
 +
 +        File topologyFile = null;
 +        try {
 +            File destDir = new 
File(System.getProperty("java.io.tmpdir")).getCanonicalFile();
 +
 +            Map<String, Map<String, String>> serviceParameters = new 
HashMap<>();
 +            Map<String, String> knoxssoParams = new HashMap<>();
 +            knoxssoParams.put("knoxsso.cookie.secure.only", "true");
 +            knoxssoParams.put("knoxsso.token.ttl", "100000");
 +            serviceParameters.put("KNOXSSO", knoxssoParams);
 +
 +            // Mock out the simple descriptor
 +            SimpleDescriptor testDescriptor = 
EasyMock.createNiceMock(SimpleDescriptor.class);
 +            
EasyMock.expect(testDescriptor.getName()).andReturn("mysimpledescriptor").anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryAddress()).andReturn(address).anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryType()).andReturn(type).anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryUser()).andReturn(null).anyTimes();
 +            
EasyMock.expect(testDescriptor.getProviderConfig()).andReturn(providerConfig.getAbsolutePath()).anyTimes();
 +            
EasyMock.expect(testDescriptor.getClusterName()).andReturn(clusterName).anyTimes();
 +            List<SimpleDescriptor.Service> serviceMocks = new ArrayList<>();
 +            for (String serviceName : serviceURLs.keySet()) {
 +                SimpleDescriptor.Service svc = 
EasyMock.createNiceMock(SimpleDescriptor.Service.class);
 +                
EasyMock.expect(svc.getName()).andReturn(serviceName).anyTimes();
 +                
EasyMock.expect(svc.getURLs()).andReturn(serviceURLs.get(serviceName)).anyTimes();
 +                
EasyMock.expect(svc.getParams()).andReturn(serviceParameters.get(serviceName)).anyTimes();
 +                EasyMock.replay(svc);
 +                serviceMocks.add(svc);
 +            }
 +            
EasyMock.expect(testDescriptor.getServices()).andReturn(serviceMocks).anyTimes();
 +            EasyMock.replay(testDescriptor);
 +
 +            // Invoke the simple descriptor handler
 +            Map<String, File> files =
 +                           SimpleDescriptorHandler.handle(testDescriptor,
 +                                                          
providerConfig.getParentFile(), // simple desc co-located with provider config
 +                                                          destDir);
 +            topologyFile = files.get("topology");
 +
 +            // Validate the resulting topology descriptor
 +            assertTrue(topologyFile.exists());
 +
 +            // Validate the topology descriptor's correctness
 +            TopologyValidator validator = new TopologyValidator( 
topologyFile.getAbsolutePath() );
 +            if( !validator.validateTopology() ){
 +                throw new SAXException( validator.getErrorString() );
 +            }
 +
 +            XPathFactory xPathfactory = XPathFactory.newInstance();
 +            XPath xpath = xPathfactory.newXPath();
 +
 +            // Parse the topology descriptor
 +            Document topologyXml = XmlUtils.readXml(topologyFile);
 +
++            // KNOX-1105 Mark generated topology files
++            assertThat("Expected the \"generated\" marker element in the 
topology XML, with value of \"true\".",
++                       topologyXml,
++                       hasXPath("/topology/generated", is("true")));
++
 +            // Validate the provider configuration
 +            Document extProviderConf = XmlUtils.readXml(new 
ByteArrayInputStream(TEST_PROVIDER_CONFIG.getBytes()));
 +            Node gatewayNode = (Node) 
xpath.compile("/topology/gateway").evaluate(topologyXml, XPathConstants.NODE);
 +            assertTrue("Resulting provider config should be identical to the 
referenced content.",
 +                       
extProviderConf.getDocumentElement().isEqualNode(gatewayNode));
 +
 +            // Validate the service declarations
 +            Map<String, List<String>> topologyServiceURLs = new HashMap<>();
 +            NodeList serviceNodes =
 +                        (NodeList) 
xpath.compile("/topology/service").evaluate(topologyXml, 
XPathConstants.NODESET);
 +            for (int serviceNodeIndex=0; serviceNodeIndex < 
serviceNodes.getLength(); serviceNodeIndex++) {
 +                Node serviceNode = serviceNodes.item(serviceNodeIndex);
 +
 +                // Validate the role
 +                Node roleNode = (Node) 
xpath.compile("role/text()").evaluate(serviceNode, XPathConstants.NODE);
 +                assertNotNull(roleNode);
 +                String role = roleNode.getNodeValue();
 +
 +                // Validate the URLs
 +                NodeList urlNodes = (NodeList) 
xpath.compile("url/text()").evaluate(serviceNode, XPathConstants.NODESET);
 +                for(int urlNodeIndex = 0 ; urlNodeIndex < 
urlNodes.getLength(); urlNodeIndex++) {
 +                    Node urlNode = urlNodes.item(urlNodeIndex);
 +                    assertNotNull(urlNode);
 +                    String url = urlNode.getNodeValue();
 +
 +                    // If the service should have a URL (some don't require 
it)
 +                    if (serviceURLs.containsKey(role)) {
 +                        assertNotNull("Declared service should have a URL.", 
url);
 +                        if (!topologyServiceURLs.containsKey(role)) {
 +                            topologyServiceURLs.put(role, new ArrayList<>());
 +                        }
 +                        topologyServiceURLs.get(role).add(url); // Add it for 
validation later
 +                    }
 +                }
 +
 +                // If params were declared in the descriptor, then validate 
them in the resulting topology file
 +                Map<String, String> params = serviceParameters.get(role);
 +                if (params != null) {
 +                    NodeList paramNodes = (NodeList) 
xpath.compile("param").evaluate(serviceNode, XPathConstants.NODESET);
 +                    for (int paramNodeIndex = 0; paramNodeIndex < 
paramNodes.getLength(); paramNodeIndex++) {
 +                        Node paramNode = paramNodes.item(paramNodeIndex);
 +                        String paramName = (String) 
xpath.compile("name/text()").evaluate(paramNode, XPathConstants.STRING);
 +                        String paramValue = (String) 
xpath.compile("value/text()").evaluate(paramNode, XPathConstants.STRING);
 +                        assertTrue(params.keySet().contains(paramName));
 +                        assertEquals(params.get(paramName), paramValue);
 +                    }
 +                }
 +
 +            }
 +            assertEquals("Unexpected number of service declarations.", 
(serviceURLs.size() - 1), topologyServiceURLs.size());
 +
 +        } catch (Exception e) {
 +            e.printStackTrace();
 +            fail(e.getMessage());
 +        } finally {
 +            providerConfig.delete();
 +            discoveryConfig.delete();
 +            if (topologyFile != null) {
 +                topologyFile.delete();
 +            }
 +        }
 +    }
 +
 +
 +    /**
 +     * KNOX-1006
 +     *
 +     * Verify the behavior of the SimpleDescriptorHandler when service 
discovery fails to produce a valid URL for
 +     * a service.
 +     *
 +     * N.B. This test depends on the PropertiesFileServiceDiscovery extension 
being configured:
 +     *             
org.apache.knox.gateway.topology.discovery.test.extension.PropertiesFileServiceDiscovery
 +     */
 +    @Test
 +    public void testInvalidServiceURLFromDiscovery() throws Exception {
 +        final String CLUSTER_NAME = "myproperties";
 +
 +        // Configure the PropertiesFile Service Discovery implementation for 
this test
 +        final String DEFAULT_VALID_SERVICE_URL = 
"http://localhost:9999/thiswillwork";;
 +        Properties serviceDiscoverySourceProps = new Properties();
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".NAMENODE",
 +                                                
DEFAULT_VALID_SERVICE_URL.replace("http", "hdfs"));
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".JOBTRACKER",
 +                                                
DEFAULT_VALID_SERVICE_URL.replace("http", "rpc"));
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".WEBHDFS",    
     DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".WEBHCAT",    
     DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".OOZIE",      
     DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".WEBHBASE",   
     DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".HIVE",       
     "{SCHEME}://localhost:10000/");
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + 
".RESOURCEMANAGER", DEFAULT_VALID_SERVICE_URL);
 +        serviceDiscoverySourceProps.setProperty(CLUSTER_NAME + ".AMBARIUI",   
     DEFAULT_VALID_SERVICE_URL);
 +        File serviceDiscoverySource = 
File.createTempFile("service-discovery", ".properties");
 +        serviceDiscoverySourceProps.store(new 
FileOutputStream(serviceDiscoverySource),
 +                                          "Test Service Discovery Source");
 +
 +        // Prepare a mock SimpleDescriptor
 +        final String type = "PROPERTIES_FILE";
 +        final String address = serviceDiscoverySource.getAbsolutePath();
 +        final Map<String, List<String>> serviceURLs = new HashMap<>();
 +        serviceURLs.put("NAMENODE", null);
 +        serviceURLs.put("JOBTRACKER", null);
 +        serviceURLs.put("WEBHDFS", null);
 +        serviceURLs.put("WEBHCAT", null);
 +        serviceURLs.put("OOZIE", null);
 +        serviceURLs.put("WEBHBASE", null);
 +        serviceURLs.put("HIVE", null);
 +        serviceURLs.put("RESOURCEMANAGER", null);
 +        serviceURLs.put("AMBARIUI", 
Collections.singletonList("http://c6401.ambari.apache.org:8080";));
 +
 +        // Write the externalized provider config to a temp file
 +        File providerConfig = 
writeProviderConfig("ambari-cluster-policy.xml", TEST_PROVIDER_CONFIG);
 +
 +        File topologyFile = null;
 +        try {
 +            File destDir = (new File(".")).getCanonicalFile();
 +
 +            // Mock out the simple descriptor
 +            SimpleDescriptor testDescriptor = 
EasyMock.createNiceMock(SimpleDescriptor.class);
 +            
EasyMock.expect(testDescriptor.getName()).andReturn("mysimpledescriptor").anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryAddress()).andReturn(address).anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryType()).andReturn(type).anyTimes();
 +            
EasyMock.expect(testDescriptor.getDiscoveryUser()).andReturn(null).anyTimes();
 +            
EasyMock.expect(testDescriptor.getProviderConfig()).andReturn(providerConfig.getAbsolutePath()).anyTimes();
 +            
EasyMock.expect(testDescriptor.getClusterName()).andReturn(CLUSTER_NAME).anyTimes();
 +            List<SimpleDescriptor.Service> serviceMocks = new ArrayList<>();
 +            for (String serviceName : serviceURLs.keySet()) {
 +                SimpleDescriptor.Service svc = 
EasyMock.createNiceMock(SimpleDescriptor.Service.class);
 +                
EasyMock.expect(svc.getName()).andReturn(serviceName).anyTimes();
 +                
EasyMock.expect(svc.getURLs()).andReturn(serviceURLs.get(serviceName)).anyTimes();
 +                EasyMock.replay(svc);
 +                serviceMocks.add(svc);
 +            }
 +            
EasyMock.expect(testDescriptor.getServices()).andReturn(serviceMocks).anyTimes();
 +            EasyMock.replay(testDescriptor);
 +
 +            // Invoke the simple descriptor handler
 +            Map<String, File> files =
 +                    SimpleDescriptorHandler.handle(testDescriptor,
 +                                                   
providerConfig.getParentFile(), // simple desc co-located with provider config
 +                                                   destDir);
 +
 +            topologyFile = files.get("topology");
 +
 +            // Validate the resulting topology descriptor
 +            assertTrue(topologyFile.exists());
 +
 +            // Validate the topology descriptor's correctness
 +            TopologyValidator validator = new TopologyValidator( 
topologyFile.getAbsolutePath() );
 +            if( !validator.validateTopology() ){
 +                throw new SAXException( validator.getErrorString() );
 +            }
 +
 +            XPathFactory xPathfactory = XPathFactory.newInstance();
 +            XPath xpath = xPathfactory.newXPath();
 +
 +            // Parse the topology descriptor
 +            Document topologyXml = XmlUtils.readXml(topologyFile);
 +
 +            // Validate the provider configuration
 +            Document extProviderConf = XmlUtils.readXml(new 
ByteArrayInputStream(TEST_PROVIDER_CONFIG.getBytes()));
 +            Node gatewayNode = (Node) 
xpath.compile("/topology/gateway").evaluate(topologyXml, XPathConstants.NODE);
 +            assertTrue("Resulting provider config should be identical to the 
referenced content.",
 +                    
extProviderConf.getDocumentElement().isEqualNode(gatewayNode));
 +
 +            // Validate the service declarations
 +            List<String> topologyServices = new ArrayList<>();
 +            Map<String, List<String>> topologyServiceURLs = new HashMap<>();
 +            NodeList serviceNodes =
 +                    (NodeList) 
xpath.compile("/topology/service").evaluate(topologyXml, 
XPathConstants.NODESET);
 +            for (int serviceNodeIndex=0; serviceNodeIndex < 
serviceNodes.getLength(); serviceNodeIndex++) {
 +                Node serviceNode = serviceNodes.item(serviceNodeIndex);
 +                Node roleNode = (Node) 
xpath.compile("role/text()").evaluate(serviceNode, XPathConstants.NODE);
 +                assertNotNull(roleNode);
 +                String role = roleNode.getNodeValue();
 +                topologyServices.add(role);
 +                NodeList urlNodes = (NodeList) 
xpath.compile("url/text()").evaluate(serviceNode, XPathConstants.NODESET);
 +                for(int urlNodeIndex = 0 ; urlNodeIndex < 
urlNodes.getLength(); urlNodeIndex++) {
 +                    Node urlNode = urlNodes.item(urlNodeIndex);
 +                    assertNotNull(urlNode);
 +                    String url = urlNode.getNodeValue();
 +                    assertNotNull("Every declared service should have a 
URL.", url);
 +                    if (!topologyServiceURLs.containsKey(role)) {
 +                        topologyServiceURLs.put(role, new ArrayList<>());
 +                    }
 +                    topologyServiceURLs.get(role).add(url);
 +                }
 +            }
 +
 +            // There should not be a service element for HIVE, since it had 
no valid URLs
 +            assertEquals("Unexpected number of service declarations.", 
serviceURLs.size() - 1, topologyServices.size());
 +            assertFalse("The HIVE service should have been omitted from the 
generated topology.", topologyServices.contains("HIVE"));
 +
 +            assertEquals("Unexpected number of service URLs.", 
serviceURLs.size() - 1, topologyServiceURLs.size());
 +
 +        } catch (Exception e) {
 +            e.printStackTrace();
 +            fail(e.getMessage());
 +        } finally {
 +            serviceDiscoverySource.delete();
 +            providerConfig.delete();
 +            if (topologyFile != null) {
 +                topologyFile.delete();
 +            }
 +        }
 +    }
 +
 +
 +    private File writeProviderConfig(String path, String content) throws 
IOException {
 +        File f = new File(path);
 +        FileUtils.write(f, content);
 +        return f;
 +    }
 +
 +}

Reply via email to