You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by tb...@apache.org on 2014/02/04 23:40:13 UTC

git commit: AMBARI-4517 - Allow for external resources in Ambari Views

Updated Branches:
  refs/heads/trunk 6d9f7e871 -> 61618f73c


AMBARI-4517 - Allow for external resources in Ambari Views


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/61618f73
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/61618f73
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/61618f73

Branch: refs/heads/trunk
Commit: 61618f73c6478c0e2a460c3b0a6ae7fcc0d33885
Parents: 6d9f7e8
Author: tbeerbower <tb...@hortonworks.com>
Authored: Tue Feb 4 04:42:01 2014 -0800
Committer: tbeerbower <tb...@hortonworks.com>
Committed: Tue Feb 4 14:39:56 2014 -0800

----------------------------------------------------------------------
 .../ViewExternalSubResourceDefinition.java      |  60 ++++++
 .../ViewExternalSubResourceService.java         | 135 +++++++++++++
 .../api/services/ViewInstanceService.java       |   5 +
 .../internal/ViewInstanceResourceProvider.java  |   4 +
 .../ambari/server/view/ViewDefinition.java      |  51 ++++-
 .../view/ViewExternalSubResourceProvider.java   | 193 +++++++++++++++++++
 .../apache/ambari/server/view/ViewRegistry.java |  53 +++--
 .../server/view/ViewSubResourceDefinition.java  |  14 +-
 .../server/view/ViewSubResourceProvider.java    |   2 +-
 .../view/configuration/ResourceConfig.java      |  15 ++
 .../server/view/configuration/ViewConfig.java   |  12 +-
 .../ViewExternalSubResourceDefinitionTest.java  |  51 +++++
 .../ViewExternalSubResourceServiceTest.java     |  50 +++++
 .../ambari/server/view/ViewDefinitionTest.java  |  31 +--
 .../ambari/server/view/ViewRegistryTest.java    |   6 +
 15 files changed, 638 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ViewExternalSubResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ViewExternalSubResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ViewExternalSubResourceDefinition.java
new file mode 100644
index 0000000..83577f2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ViewExternalSubResourceDefinition.java
@@ -0,0 +1,60 @@
+/**
+ * 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.ambari.server.api.resources;
+
+import org.apache.ambari.server.controller.spi.Resource;
+
+import java.util.Collections;
+import java.util.Set;
+
+
+/**
+ * View external sub-resource definition.
+ */
+public class ViewExternalSubResourceDefinition extends BaseResourceDefinition {
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct a view resource definition.
+   *
+   * @param type  the resource type
+   */
+  public ViewExternalSubResourceDefinition(Resource.Type type) {
+    super(type);
+  }
+
+
+  // ----- ResourceDefinition ------------------------------------------------
+
+  @Override
+  public String getPluralName() {
+    return "resources";
+  }
+
+  @Override
+  public String getSingularName() {
+    return "resource";
+  }
+
+  @Override
+  public Set<SubResourceDefinition> getSubResourceDefinitions() {
+    return Collections.emptySet();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewExternalSubResourceService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewExternalSubResourceService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewExternalSubResourceService.java
new file mode 100644
index 0000000..7ce8b97
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewExternalSubResourceService.java
@@ -0,0 +1,135 @@
+/**
+ * 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.ambari.server.api.services;
+
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.view.ViewInstanceDefinition;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Service responsible for view sub-resource requests.
+ */
+public class ViewExternalSubResourceService  extends BaseService {
+
+  /**
+   * The resource type.
+   */
+  private final Resource.Type type;
+
+  /**
+   * The view name.
+   */
+  private final String viewName;
+
+  /**
+   * The instance name.
+   */
+  private final String instanceName;
+
+  /**
+   * Mapping of resource names to services.
+   */
+  private final Map<String, Object> resourceServiceMap = new HashMap<String, Object>();
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  public ViewExternalSubResourceService(Resource.Type type, ViewInstanceDefinition viewInstanceDefinition) {
+    this.type         = type;
+    this.viewName     = viewInstanceDefinition.getViewDefinition().getName();
+    this.instanceName = viewInstanceDefinition.getName();
+  }
+
+  /**
+   * Handles URL: /resources
+   * Get all external resources for a view.
+   *
+   * @param headers  http headers
+   * @param ui       uri info
+   *
+   * @return instance collection resource representation
+   */
+  @GET
+  @Produces("text/plain")
+  public Response getResources(@Context HttpHeaders headers, @Context UriInfo ui) {
+    return handleRequest(headers, null, ui, Request.Type.GET,
+        createServiceResource(viewName, instanceName));
+  }
+
+  /**
+   * Handles: GET /resources/{resourceName} Get a specific external resource.
+   *
+   * @param resourceName  resource name
+   *
+   * @return resource service instance representation
+   *
+   * @throws IllegalArgumentException if the given resource name is unknown
+   */
+  @Path("{resourceName}")
+  public Object getResource(@PathParam("resourceName") String resourceName) throws IOException {
+
+    Object service = resourceServiceMap.get(resourceName);
+    if (service == null) {
+      throw new IllegalArgumentException("A resource type " + resourceName + " for view instance " +
+          viewName + "/" + instanceName + " can not be found.");
+    }
+
+    return service;
+  }
+
+
+  // ----- helper methods ----------------------------------------------------
+
+  /**
+   * Register a sub-resource service.
+   *
+   * @param resourceName  the resource name
+   * @param service       the service
+   */
+  public void addResourceService(String resourceName, Object service) {
+    resourceServiceMap.put(resourceName, service);
+  }
+
+  /**
+   * Create an view instance resource.
+   *
+   * @param viewName      view name
+   * @param instanceName  instance name
+   *
+   * @return a view instance resource
+   */
+  private ResourceInstance createServiceResource(String viewName, String instanceName) {
+    Map<Resource.Type,String> mapIds = new HashMap<Resource.Type, String>();
+    mapIds.put(Resource.Type.View, viewName);
+    mapIds.put(Resource.Type.ViewInstance, instanceName);
+    return createResource(type, mapIds);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewInstanceService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewInstanceService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewInstanceService.java
index 96bc863..f0c32bf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewInstanceService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewInstanceService.java
@@ -74,6 +74,11 @@ public class ViewInstanceService extends BaseService {
   public Response getService(@Context HttpHeaders headers, @Context UriInfo ui,
                              @PathParam("instanceName") String instanceName) {
 
+    if (ViewRegistry.getInstance().getInstanceDefinition(m_viewName, instanceName) == null) {
+      throw new IllegalArgumentException("A view instance " +
+          m_viewName + "/" + instanceName + " can not be found.");
+    }
+
     return handleRequest(headers, null, ui, Request.Type.GET,
         createServiceResource(m_viewName, instanceName));
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java
index 82f2224..7efea04 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java
@@ -47,6 +47,7 @@ public class ViewInstanceResourceProvider extends AbstractResourceProvider {
    */
   public static final String VIEW_NAME_PROPERTY_ID        = "ViewInstanceInfo/view_name";
   public static final String INSTANCE_NAME_PROPERTY_ID    = "ViewInstanceInfo/instance_name";
+  public static final String PROPERTIES_PROPERTY_ID       = "ViewInstanceInfo/properties";
   public static final String SERVLET_MAPPINGS_PROPERTY_ID = "ViewInstanceInfo/servlet_mappings";
 
   /**
@@ -65,6 +66,7 @@ public class ViewInstanceResourceProvider extends AbstractResourceProvider {
   static {
     propertyIds.add(VIEW_NAME_PROPERTY_ID);
     propertyIds.add(INSTANCE_NAME_PROPERTY_ID);
+    propertyIds.add(PROPERTIES_PROPERTY_ID);
     propertyIds.add(SERVLET_MAPPINGS_PROPERTY_ID);
   }
 
@@ -114,6 +116,8 @@ public class ViewInstanceResourceProvider extends AbstractResourceProvider {
 
               setResourceProperty(resource, VIEW_NAME_PROPERTY_ID, viewDefinition.getName(), requestedIds);
               setResourceProperty(resource, INSTANCE_NAME_PROPERTY_ID, viewInstanceDefinition.getName(), requestedIds);
+              setResourceProperty(resource, PROPERTIES_PROPERTY_ID,
+                  viewInstanceDefinition.getProperties(), requestedIds);
               setResourceProperty(resource, SERVLET_MAPPINGS_PROPERTY_ID,
                   viewInstanceDefinition.getServletMappings(), requestedIds);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDefinition.java
index 80651ad..8a60667 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewDefinition.java
@@ -18,7 +18,6 @@
 
 package org.apache.ambari.server.view;
 
-import org.apache.ambari.server.api.resources.BaseResourceDefinition;
 import org.apache.ambari.server.view.configuration.ResourceConfig;
 import org.apache.ambari.server.view.configuration.ViewConfig;
 import org.apache.ambari.server.controller.spi.Resource;
@@ -46,7 +45,7 @@ public class ViewDefinition {
   /**
    * The mapping of resource type to resource definition.
    */
-  private final Map<Resource.Type, BaseResourceDefinition> resourceDefinitions = new HashMap<Resource.Type, BaseResourceDefinition>();
+  private final Map<Resource.Type, ViewSubResourceDefinition> resourceDefinitions = new HashMap<Resource.Type, ViewSubResourceDefinition>();
 
   /**
    * The mapping of resource type to resource configuration.
@@ -59,6 +58,12 @@ public class ViewDefinition {
   private final Map<String, ViewInstanceDefinition> instanceDefinitions = new HashMap<String, ViewInstanceDefinition>();
 
 
+  /**
+   * The external resource type for the view.
+   */
+  private final Resource.Type externalResourceType;
+
+
   // ----- Constructors ------------------------------------------------------
 
   /**
@@ -68,6 +73,9 @@ public class ViewDefinition {
    */
   public ViewDefinition(ViewConfig configuration) {
     this.configuration = configuration;
+
+    this.externalResourceType =
+        new Resource.Type(getQualifiedResourceTypeName(ResourceConfig.EXTERNAL_RESOURCE_PLURAL_NAME));
   }
 
 
@@ -135,22 +143,31 @@ public class ViewDefinition {
    *
    * @param definition  the resource definition
    */
-  public void addResourceDefinition(BaseResourceDefinition definition) {
+  public void addResourceDefinition(ViewSubResourceDefinition definition) {
     resourceDefinitions.put(definition.getType(), definition);
   }
 
   /**
-   * Get the resource definition for the given type
+   * Get the resource definition for the given type.
    *
    * @param type  the resource type
    *
    * @return the resource definition associated with the given type
    */
-  public BaseResourceDefinition getResourceDefinition(Resource.Type type) {
+  public ViewSubResourceDefinition getResourceDefinition(Resource.Type type) {
     return resourceDefinitions.get(type);
   }
 
   /**
+   * Get the mapping of resource type to resource definitions.
+   *
+   * @return the mapping of resource type to resource definitions
+   */
+  public Map<Resource.Type, ViewSubResourceDefinition> getResourceDefinitions() {
+    return resourceDefinitions;
+  }
+
+  /**
    * Add a resource configuration for the given type.
    *
    * @param type    the resource type
@@ -161,7 +178,7 @@ public class ViewDefinition {
   }
 
   /**
-   * Get a mapping of resource type to resource configurations.
+   * Get the mapping of resource type to resource configurations.
    *
    * @return the mapping of resource types to resource configurations
    */
@@ -175,7 +192,7 @@ public class ViewDefinition {
    * @return the set of resource type
    */
   public Set<Resource.Type> getViewResourceTypes() {
-    return resourceConfigurations.keySet();
+    return resourceProviders.keySet();
   }
 
   /**
@@ -206,4 +223,24 @@ public class ViewDefinition {
   public ViewInstanceDefinition getInstanceDefinition(String instanceName) {
     return instanceDefinitions.get(instanceName);
   }
+
+  /**
+   * Get the external resource type for the view.
+   *
+   * @return the external resource type
+   */
+  public Resource.Type getExternalResourceType() {
+    return externalResourceType;
+  }
+
+  /**
+   * Get a resource name qualified by the associated view name.
+   *
+   * @param resourceTypeName  the resource type name
+   *
+   * @return the qualified resource name
+   */
+  public String getQualifiedResourceTypeName(String resourceTypeName) {
+    return configuration.getName() + "/" + resourceTypeName;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExternalSubResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExternalSubResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExternalSubResourceProvider.java
new file mode 100644
index 0000000..d3371b6
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExternalSubResourceProvider.java
@@ -0,0 +1,193 @@
+/**
+ * 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.ambari.server.view;
+
+import org.apache.ambari.server.controller.internal.AbstractResourceProvider;
+import org.apache.ambari.server.controller.internal.ResourceImpl;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * External view sub-resource provider.
+ */
+public class ViewExternalSubResourceProvider extends AbstractResourceProvider {
+
+  /**
+   * View external sub resource property id constants.
+   */
+  private static final String VIEW_NAME_PROPERTY_ID     = "view_name";
+  private static final String INSTANCE_NAME_PROPERTY_ID = "instance_name";
+  private static final String RESOURCE_NAME_PROPERTY_ID = "name";
+
+  /**
+   * The resource type.
+   */
+  private final Resource.Type type;
+
+  /**
+   * The names of the external resources for the view.
+   */
+  private final Set<String> resourceNames = new HashSet<String>();
+
+  /**
+   * The set of key property ids.
+   */
+  private final Set<String> pkPropertyIds;
+
+  /**
+   * The associated view definition.
+   */
+  private final ViewDefinition viewDefinition;
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct a view sub-resource provider.
+   *
+   * @param type            the resource type
+   * @param viewDefinition  the associated view definition
+   */
+  public ViewExternalSubResourceProvider(Resource.Type type, ViewDefinition viewDefinition) {
+    super(_getPropertyIds(), _getKeyPropertyIds(type));
+
+    this.type           = type;
+    this.pkPropertyIds  = new HashSet<String>(getKeyPropertyIds().values());
+    this.viewDefinition = viewDefinition;
+  }
+
+  @Override
+  public RequestStatus createResources(Request request)
+      throws SystemException, UnsupportedPropertyException,
+      ResourceAlreadyExistsException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Not supported!");
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+
+    Set<Resource> resourceSet = new HashSet<Resource>();
+
+    Set<ViewInstanceDefinition> instanceDefinitions = new HashSet<ViewInstanceDefinition>();
+
+    Set<Map<String, Object>> propertyMaps = getPropertyMaps(predicate);
+    int size = propertyMaps.size();
+
+    Collection<ViewInstanceDefinition> viewInstanceDefinitions = viewDefinition.getInstanceDefinitions();
+    if (size == 0) {
+      instanceDefinitions.addAll(viewInstanceDefinitions);
+    } else {
+      for (Map<String, Object> propertyMap : propertyMaps) {
+        String instanceName = (String) propertyMap.get(INSTANCE_NAME_PROPERTY_ID);
+        if (instanceName == null) {
+          instanceDefinitions.addAll(viewInstanceDefinitions);
+          break;
+        } else {
+          instanceDefinitions.add(viewDefinition.getInstanceDefinition(instanceName));
+        }
+      }
+    }
+
+    for (ViewInstanceDefinition viewInstanceDefinition : instanceDefinitions) {
+      for (String resourceName : resourceNames) {
+        ResourceImpl resource = new ResourceImpl(type);
+        resource.setProperty(VIEW_NAME_PROPERTY_ID, viewDefinition.getName());
+        resource.setProperty(INSTANCE_NAME_PROPERTY_ID, viewInstanceDefinition.getName());
+        resource.setProperty("name", resourceName);
+        resourceSet.add(resource);
+      }
+    }
+
+    return resourceSet;
+  }
+
+  @Override
+  public RequestStatus updateResources(Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Not supported!");
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Not supported!");
+  }
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return pkPropertyIds;
+  }
+
+  @Override
+  public Set<String> checkPropertyIds(Set<String> propertyIds) {
+    return Collections.emptySet();
+  }
+
+
+  // ----- helper methods ----------------------------------------------------
+
+  /**
+   * Register an external sub-resource name.
+   *
+   * @param resourceName  the resource name
+   */
+  public void addResourceName(String resourceName) {
+    resourceNames.add(resourceName);
+  }
+
+  // get the key property ids for the resource
+  private static Map<Resource.Type, String> _getKeyPropertyIds(Resource.Type type) {
+
+    Map<Resource.Type, String> keyPropertyIds = new HashMap<Resource.Type, String>();
+
+    keyPropertyIds.put(Resource.Type.View, VIEW_NAME_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.ViewInstance, INSTANCE_NAME_PROPERTY_ID);
+    keyPropertyIds.put(type, RESOURCE_NAME_PROPERTY_ID);
+
+    return keyPropertyIds;
+  }
+
+  // get the property ids for the resource
+  private static Set<String> _getPropertyIds()  {
+    Set<String> propertyIds = new HashSet<String>();
+    propertyIds.add(INSTANCE_NAME_PROPERTY_ID);
+    propertyIds.add(VIEW_NAME_PROPERTY_ID);
+    propertyIds.add(RESOURCE_NAME_PROPERTY_ID);
+
+    return propertyIds;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
index 51fe2b6..a653415 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
@@ -21,9 +21,10 @@ package org.apache.ambari.server.view;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import org.apache.ambari.server.api.resources.BaseResourceDefinition;
 import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl;
 import org.apache.ambari.server.api.resources.SubResourceDefinition;
+import org.apache.ambari.server.api.resources.ViewExternalSubResourceDefinition;
+import org.apache.ambari.server.api.services.ViewExternalSubResourceService;
 import org.apache.ambari.server.api.services.ViewSubResourceService;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.spi.Resource;
@@ -263,20 +264,33 @@ public class ViewRegistry {
 
     ViewDefinition viewDefinition = new ViewDefinition(viewConfig);
 
+    Resource.Type externalResourceType = viewDefinition.getExternalResourceType();
+
+    ViewExternalSubResourceProvider viewExternalSubResourceProvider =
+        new ViewExternalSubResourceProvider(externalResourceType, viewDefinition);
+    viewDefinition.addResourceProvider(externalResourceType, viewExternalSubResourceProvider );
+
+    ResourceInstanceFactoryImpl.addResourceDefinition(externalResourceType,
+        new ViewExternalSubResourceDefinition(externalResourceType));
+
     for (ResourceConfig resourceConfiguration : resourceConfigurations) {
 
-      BaseResourceDefinition resourceDefinition = new ViewSubResourceDefinition(viewDefinition, resourceConfiguration);
+      ViewSubResourceDefinition resourceDefinition = new ViewSubResourceDefinition(viewDefinition, resourceConfiguration);
       viewDefinition.addResourceDefinition(resourceDefinition);
 
       Resource.Type type = resourceDefinition.getType();
-      ResourceInstanceFactoryImpl.addResourceDefinition(type, resourceDefinition);
-
       viewDefinition.addResourceConfiguration(type, resourceConfiguration);
 
-      Class<?> clazz      = resourceConfiguration.getResourceClass(cl);
-      String   idProperty = resourceConfiguration.getIdProperty();
+      if (resourceConfiguration.isExternal()) {
+        viewExternalSubResourceProvider.addResourceName(resourceConfiguration.getName());
+      } else {
+        ResourceInstanceFactoryImpl.addResourceDefinition(type, resourceDefinition);
+
+        Class<?> clazz      = resourceConfiguration.getResourceClass(cl);
+        String   idProperty = resourceConfiguration.getIdProperty();
 
-      viewDefinition.addResourceProvider(type, new ViewSubResourceProvider(type, clazz, idProperty, viewDefinition));
+        viewDefinition.addResourceProvider(type, new ViewSubResourceProvider(type, clazz, idProperty, viewDefinition));
+      }
     }
 
     ViewRegistry.getInstance().addDefinition(viewDefinition);
@@ -301,20 +315,29 @@ public class ViewRegistry {
 
     ViewContext viewInstanceContext = new ViewContextImpl(viewInstanceDefinition);
 
-    Map<Resource.Type, ResourceConfig> resourceConfigurations = viewDefinition.getResourceConfigurations();
+    ViewExternalSubResourceService externalSubResourceService =
+        new ViewExternalSubResourceService(viewDefinition.getExternalResourceType(), viewInstanceDefinition);
+
+    viewInstanceDefinition.addService(ResourceConfig.EXTERNAL_RESOURCE_PLURAL_NAME, externalSubResourceService);
 
-    for (Map.Entry<Resource.Type, ResourceConfig> entry : resourceConfigurations.entrySet()) {
+    Collection<ViewSubResourceDefinition> resourceDefinitions = viewDefinition.getResourceDefinitions().values();
+    for (ViewSubResourceDefinition resourceDefinition : resourceDefinitions) {
 
-      Resource.Type  type          = entry.getKey();
-      ResourceConfig configuration = entry.getValue();
+      Resource.Type  type           = resourceDefinition.getType();
+      ResourceConfig resourceConfig = resourceDefinition.getResourceConfiguration();
 
       ViewResourceHandler viewResourceService =
           new ViewSubResourceService(type, viewDefinition.getName(), instanceConfig.getName());
-      viewInstanceDefinition.addService(viewDefinition.getResourceDefinition(type).getPluralName(),
-          getService(configuration.getServiceClass(cl), viewResourceService, viewInstanceContext));
 
-      viewInstanceDefinition.addResourceProvider(type,
-          getProvider(configuration.getProviderClass(cl), viewInstanceContext));
+      Object service = getService(resourceConfig.getServiceClass(cl), viewResourceService, viewInstanceContext);
+
+      if (resourceConfig.isExternal()) {
+        externalSubResourceService.addResourceService(resourceConfig.getName(), service);
+      } else {
+        viewInstanceDefinition.addService(viewDefinition.getResourceDefinition(type).getPluralName(),service);
+        viewInstanceDefinition.addResourceProvider(type,
+            getProvider(resourceConfig.getProviderClass(cl), viewInstanceContext));
+      }
     }
 
     ViewConfig viewConfig = viewDefinition.getConfiguration();

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceDefinition.java
index 9ea6eb0..ac7f091 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceDefinition.java
@@ -56,7 +56,7 @@ public class ViewSubResourceDefinition extends BaseResourceDefinition {
    * @param resourceConfiguration  the resource configuration
    */
   public ViewSubResourceDefinition(ViewDefinition viewDefinition, ResourceConfig resourceConfiguration) {
-    super(new Resource.Type(getQualifiedResourceTypeName(viewDefinition.getName(), resourceConfiguration.getName())));
+    super(new Resource.Type(viewDefinition.getQualifiedResourceTypeName(resourceConfiguration.getName())));
 
     this.viewDefinition        = viewDefinition;
     this.resourceConfiguration = resourceConfiguration;
@@ -83,7 +83,7 @@ public class ViewSubResourceDefinition extends BaseResourceDefinition {
       if (subResourceNames != null) {
         for (String subType : subResourceNames) {
           Resource.Type type = Resource.Type.valueOf(
-              getQualifiedResourceTypeName(viewDefinition.getName(), subType));
+              viewDefinition.getQualifiedResourceTypeName(subType));
           definitions.add(new SubResourceDefinition(type));
         }
       }
@@ -94,8 +94,12 @@ public class ViewSubResourceDefinition extends BaseResourceDefinition {
 
   // ----- helper methods ----------------------------------------------------
 
-  // qualify the resource type name with the view name
-  private static String getQualifiedResourceTypeName(String viewName, String resourceTypeName) {
-    return viewName + "/" + resourceTypeName;
+  /**
+   * Get the associated resource configuration.
+   *
+   * @return the resource configuration
+   */
+  public ResourceConfig getResourceConfiguration() {
+    return resourceConfiguration;
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceProvider.java
index 9a275d3..4241e8f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewSubResourceProvider.java
@@ -261,7 +261,7 @@ public class ViewSubResourceProvider extends AbstractResourceProvider {
   private static Set<String> discoverPropertyIds(Class<?> clazz) throws IntrospectionException {
     Set<String> propertyIds = new HashSet<String>(getDescriptorMap(clazz).keySet());
     propertyIds.add(INSTANCE_NAME_PROPERTY_ID);
-    propertyIds.add("view_name");
+    propertyIds.add(VIEW_NAME_PROPERTY_ID);
 
     return propertyIds;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ResourceConfig.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ResourceConfig.java b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ResourceConfig.java
index 8bc3de1..5d23081 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ResourceConfig.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ResourceConfig.java
@@ -32,6 +32,12 @@ import java.util.List;
  */
 @XmlAccessorType(XmlAccessType.FIELD)
 public class ResourceConfig {
+
+  /**
+   * Constants.
+   */
+  public static final String EXTERNAL_RESOURCE_PLURAL_NAME = "resources";
+
   /**
    * The resource name.
    */
@@ -171,4 +177,13 @@ public class ResourceConfig {
     }
     return resourceClass;
   }
+
+  /**
+   * Determine whether the resource is external (does not use the API framework).
+   *
+   * @return true if the resource is external.
+   */
+  public boolean isExternal() {
+    return resource == null || provider == null;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java
index daac81f..90e4a24 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java
@@ -173,8 +173,10 @@ public class ViewConfig {
       throws ClassNotFoundException{
     if (servletPathMap == null) {
       servletPathMap = new HashMap<String, Class<? extends HttpServlet>>();
-      for (ServletConfig servletConfig : servlets) {
-        servletPathMap.put(servletConfig.getName(), servletConfig.getServletClass(cl));
+      if (servlets != null) {
+        for (ServletConfig servletConfig : servlets) {
+          servletPathMap.put(servletConfig.getName(), servletConfig.getServletClass(cl));
+        }
       }
     }
     return servletPathMap;
@@ -188,8 +190,10 @@ public class ViewConfig {
   public synchronized Map<String, String> getServletURLPatternMap() {
     if (servletURLPatternMap == null) {
       servletURLPatternMap = new HashMap<String, String>();
-      for (ServletMappingConfig servletMappingConfig : mappings) {
-        servletURLPatternMap.put(servletMappingConfig.getName(), servletMappingConfig.getUrlPattern());
+      if (mappings != null) {
+        for (ServletMappingConfig servletMappingConfig : mappings) {
+          servletURLPatternMap.put(servletMappingConfig.getName(), servletMappingConfig.getUrlPattern());
+        }
       }
     }
     return servletURLPatternMap;

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ViewExternalSubResourceDefinitionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ViewExternalSubResourceDefinitionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ViewExternalSubResourceDefinitionTest.java
new file mode 100644
index 0000000..5fa7ef4
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ViewExternalSubResourceDefinitionTest.java
@@ -0,0 +1,51 @@
+/**
+ * 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.ambari.server.api.resources;
+
+import org.apache.ambari.server.controller.spi.Resource;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Collections;
+
+/**
+ * ViewExternalSubResourceDefinition tests.
+ */
+public class ViewExternalSubResourceDefinitionTest {
+  @Test
+  public void testGetPluralName() throws Exception {
+    Resource.Type type = new Resource.Type("resource");
+    ViewExternalSubResourceDefinition definition = new ViewExternalSubResourceDefinition(type);
+    Assert.assertEquals("resources", definition.getPluralName());
+  }
+
+  @Test
+  public void testGetSingularName() throws Exception {
+    Resource.Type type = new Resource.Type("resource");
+    ViewExternalSubResourceDefinition definition = new ViewExternalSubResourceDefinition(type);
+    Assert.assertEquals("resource", definition.getSingularName());
+  }
+
+  @Test
+  public void testGetSubResourceDefinitions() throws Exception {
+    Resource.Type type = new Resource.Type("resource");
+    ViewExternalSubResourceDefinition definition = new ViewExternalSubResourceDefinition(type);
+    Assert.assertEquals(Collections.emptySet(), definition.getSubResourceDefinitions());
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewExternalSubResourceServiceTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewExternalSubResourceServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewExternalSubResourceServiceTest.java
new file mode 100644
index 0000000..40ae816
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewExternalSubResourceServiceTest.java
@@ -0,0 +1,50 @@
+/**
+ * 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.ambari.server.api.services;
+
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.view.ViewInstanceDefinition;
+import org.apache.ambari.server.view.ViewInstanceDefinitionTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * ViewExternalSubResourceService tests.
+ */
+public class ViewExternalSubResourceServiceTest {
+  @Test
+  public void testAddResourceService() throws Exception {
+    Resource.Type type = new Resource.Type("resource");
+
+    ViewInstanceDefinition definition = ViewInstanceDefinitionTest.getViewInstanceDefinition();
+    ViewExternalSubResourceService service = new ViewExternalSubResourceService(type, definition);
+
+    Object fooService = new Object();
+
+    service.addResourceService("foo", fooService);
+
+    Assert.assertEquals(fooService, service.getResource("foo"));
+
+    try {
+      service.getResource("bar");
+      Assert.fail("Expected IllegalArgumentException for unknown service name.");
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDefinitionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDefinitionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDefinitionTest.java
index 22a03ad..5cf871e 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDefinitionTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewDefinitionTest.java
@@ -78,19 +78,33 @@ public class ViewDefinitionTest {
   public void testAddGetResourceProvider() throws Exception {
     ViewDefinition viewDefinition = getViewDefinition();
 
-    ResourceProvider provider = createNiceMock(ResourceProvider.class);
+    ResourceProvider provider1 = createNiceMock(ResourceProvider.class);
 
-    Resource.Type type = new Resource.Type("myType");
-    viewDefinition.addResourceProvider(type, provider);
+    Resource.Type type1 = new Resource.Type("myType1");
+    viewDefinition.addResourceProvider(type1, provider1);
+
+    Assert.assertEquals(provider1, viewDefinition.getResourceProvider(type1));
+
+    ResourceProvider provider2 = createNiceMock(ResourceProvider.class);
+
+    Resource.Type type2 = new Resource.Type("myType2");
+    viewDefinition.addResourceProvider(type2, provider2);
+
+    Assert.assertEquals(provider2, viewDefinition.getResourceProvider(type2));
+
+    Set<Resource.Type> types = viewDefinition.getViewResourceTypes();
+
+    Assert.assertEquals(2, types.size());
 
-    Assert.assertEquals(provider, viewDefinition.getResourceProvider(type));
+    Assert.assertTrue(types.contains(type1));
+    Assert.assertTrue(types.contains(type2));
   }
 
   @Test
   public void testAddGetResourceDefinition() throws Exception {
     ViewDefinition viewDefinition = getViewDefinition();
 
-    BaseResourceDefinition definition = createNiceMock(BaseResourceDefinition.class);
+    ViewSubResourceDefinition definition = createNiceMock(ViewSubResourceDefinition.class);
     Resource.Type type = new Resource.Type("myType");
 
     expect(definition.getType()).andReturn(type);
@@ -121,13 +135,6 @@ public class ViewDefinitionTest {
     viewDefinition.addResourceConfiguration(type2, config);
 
     Assert.assertEquals(config, viewDefinition.getResourceConfigurations().get(type2));
-
-    Set<Resource.Type> types = viewDefinition.getViewResourceTypes();
-
-    Assert.assertEquals(2, types.size());
-
-    Assert.assertTrue(types.contains(type1));
-    Assert.assertTrue(types.contains(type2));
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/ambari/blob/61618f73/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java
index 754727d..20bae47 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java
@@ -20,6 +20,7 @@ package org.apache.ambari.server.view;
 
 import org.apache.ambari.server.api.resources.SubResourceDefinition;
 import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.view.configuration.ResourceConfig;
 import org.apache.ambari.server.view.configuration.ResourceConfigTest;
 import org.junit.AfterClass;
@@ -30,6 +31,8 @@ import org.junit.Test;
 import java.util.Collection;
 import java.util.Set;
 
+import static org.easymock.EasyMock.createNiceMock;
+
 /**
  * ViewRegistry tests.
  */
@@ -79,6 +82,9 @@ public class ViewRegistryTest {
     ResourceConfig config = ResourceConfigTest.getResourceConfigs().get(0);
     Resource.Type type1 = new Resource.Type("myType");
 
+    ResourceProvider provider1 = createNiceMock(ResourceProvider.class);
+    viewDefinition.addResourceProvider(type1, provider1);
+
     viewDefinition.addResourceConfiguration(type1, config);
     registry.addDefinition(viewDefinition);
     Set<SubResourceDefinition> subResourceDefinitions = registry.getSubResourceDefinitions("MY_VIEW");