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

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

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