瀏覽代碼

AMBARI-10221. Support separate themes for services defined in same metainfo.xml and multiple themes for service (mpapyrkovskyy via srimanth)

Srimanth Gunturi 10 年之前
父節點
當前提交
fb763311e2
共有 15 個文件被更改,包括 394 次插入102 次删除
  1. 4 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
  2. 1 5
      ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
  3. 36 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java
  4. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
  5. 0 63
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackArtifactResourceProvider.java
  6. 203 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ThemeArtifactResourceProvider.java
  7. 3 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
  8. 3 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
  9. 0 3
      ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java
  10. 28 5
      ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
  11. 16 7
      ambari-server/src/main/java/org/apache/ambari/server/stack/ThemeModule.java
  12. 50 17
      ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java
  13. 40 1
      ambari-server/src/main/java/org/apache/ambari/server/state/ThemeInfo.java
  14. 8 0
      ambari-server/src/main/resources/stacks/HDP/2.2/services/YARN/metainfo.xml
  15. 0 0
      ambari-server/src/main/resources/stacks/HDP/2.2/services/YARN/themes/theme.json

+ 4 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java

@@ -358,6 +358,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new SimpleResourceDefinition(Resource.Type.Artifact, "artifact", "artifacts");
         break;
 
+      case Theme:
+        resourceDefinition = new SimpleResourceDefinition(Resource.Type.Theme, "theme", "themes");
+        break;
+
       default:
         throw new IllegalArgumentException("Unsupported resource type: " + type);
     }

+ 1 - 5
ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java

@@ -90,6 +90,7 @@ public class AmbariMetaInfo {
 
 
   public static final String SERVICE_CONFIG_FOLDER_NAME = "configuration";
+  public static final String SERVICE_THEMES_FOLDER_NAME = "themes";
   public static final String SERVICE_CONFIG_FILE_NAME_POSTFIX = ".xml";
   public static final String RCO_FILE_NAME = "role_command_order.json";
   public static final String SERVICE_METRIC_FILE_NAME = "metrics.json";
@@ -100,11 +101,6 @@ public class AmbariMetaInfo {
    */
   public static final String KERBEROS_DESCRIPTOR_FILE_NAME = "kerberos.json";
 
-  /**
-   * Filename for theme file at service layer
-   */
-  public static final String SERVICE_THEME_FILE_NAME = "theme.json";
-
   /**
    * This string is used in placeholder in places that are common for
    * all operating systems or in situations where os type is not important.

+ 36 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java

@@ -178,6 +178,31 @@ public class StacksService extends BaseService {
         createStackServiceArtifactsResource(stackName, stackVersion, serviceName, null));
   }
 
+  @GET
+  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/themes")
+  @Produces("text/plain")
+  public Response getStackServiceThemes(String body, @Context HttpHeaders headers,
+                                           @Context UriInfo ui, @PathParam("stackName") String stackName,
+                                           @PathParam("stackVersion") String stackVersion,
+                                           @PathParam("serviceName") String serviceName) {
+
+    return handleRequest(headers, body, ui, Request.Type.GET,
+      createStackServiceThemesResource(stackName, stackVersion, serviceName, null));
+  }
+
+  @GET
+  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/themes/{themeName}")
+  @Produces("text/plain")
+  public Response getStackServiceTheme(String body, @Context HttpHeaders headers,
+                                           @Context UriInfo ui, @PathParam("stackName") String stackName,
+                                           @PathParam("stackVersion") String stackVersion,
+                                           @PathParam("serviceName") String serviceName,
+                                           @PathParam("themeName") String themeName) {
+
+    return handleRequest(headers, body, ui, Request.Type.GET,
+      createStackServiceThemesResource(stackName, stackVersion, serviceName, themeName));
+  }
+
   @GET
   @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/artifacts/{artifactName}")
   @Produces("text/plain")
@@ -390,6 +415,17 @@ public class StacksService extends BaseService {
     return createResource(Resource.Type.StackArtifact, mapIds);
   }
 
+  ResourceInstance createStackServiceThemesResource(String stackName, String stackVersion, String serviceName,
+                                                    String themeName) {
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+    mapIds.put(Resource.Type.Stack, stackName);
+    mapIds.put(Resource.Type.StackVersion, stackVersion);
+    mapIds.put(Resource.Type.StackService, serviceName);
+    mapIds.put(Resource.Type.Theme, themeName);
+
+    return createResource(Resource.Type.Theme, mapIds);
+  }
+
   ResourceInstance createStackResource(String stackName) {
 
     return createResource(Resource.Type.Stack,

+ 2 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java

@@ -155,6 +155,8 @@ public abstract class AbstractControllerResourceProvider extends AbstractResourc
         return resourceProviderFactory.getRepositoryVersionResourceProvider();
       case StackArtifact:
         return new StackArtifactResourceProvider(managementController);
+      case Theme:
+        return new ThemeArtifactResourceProvider(managementController);
 
       default:
         throw new IllegalArgumentException("Unknown type " + type);

+ 0 - 63
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackArtifactResourceProvider.java

@@ -122,11 +122,6 @@ public class StackArtifactResourceProvider extends AbstractControllerResourcePro
    */
   public static final String KERBEROS_DESCRIPTOR_NAME = "kerberos_descriptor";
 
-  /**
-   * Name of theme artifact
-   */
-  public static final String THEME_ARTIFACT_NAME = "theme";
-
   /**
    * name of the metrics descriptor artifact.
    */
@@ -190,7 +185,6 @@ public class StackArtifactResourceProvider extends AbstractControllerResourcePro
 
     resources.addAll(getKerberosDescriptors(request, predicate));
     resources.addAll(getMetricsDescriptors(request, predicate));
-    resources.addAll(getThemes(request, predicate));
     // add other artifacts types here
 
     if (resources.isEmpty()) {
@@ -290,63 +284,6 @@ public class StackArtifactResourceProvider extends AbstractControllerResourcePro
     return resources;
   }
 
-  private Set<Resource> getThemes(Request request, Predicate predicate) throws NoSuchParentResourceException,
-    NoSuchResourceException, UnsupportedPropertyException, SystemException {
-
-    Set<Resource> resources = new HashSet<Resource>();
-    for (Map<String, Object> properties : getPropertyMaps(predicate)) {
-      String artifactName = (String) properties.get(ARTIFACT_NAME_PROPERTY_ID);
-      if (artifactName == null || artifactName.equals(THEME_ARTIFACT_NAME)) {
-        String stackName = (String) properties.get(STACK_NAME_PROPERTY_ID);
-        String stackVersion = (String) properties.get(STACK_VERSION_PROPERTY_ID);
-        String stackService = (String) properties.get(STACK_SERVICE_NAME_PROPERTY_ID);
-
-        StackInfo stackInfo;
-        try {
-          stackInfo = getManagementController().getAmbariMetaInfo().getStack(stackName, stackVersion);
-        } catch (AmbariException e) {
-          throw new NoSuchParentResourceException(String.format(
-            "Parent stack resource doesn't exist: stackName='%s', stackVersion='%s'", stackName, stackVersion));
-        }
-
-        List<ServiceInfo> serviceInfoList = new ArrayList<ServiceInfo>();
-
-        if (stackService == null) {
-          serviceInfoList.addAll(stackInfo.getServices());
-        } else {
-          ServiceInfo service = stackInfo.getService(stackService);
-          if (service == null) {
-            throw new NoSuchParentResourceException(String.format(
-              "Parent stack/service resource doesn't exist: stackName='%s', stackVersion='%s', serviceName='%s'",
-              stackName, stackVersion, stackService));
-          }
-          serviceInfoList.add(service);
-        }
-
-        for (ServiceInfo serviceInfo : serviceInfoList) {
-          ThemeInfo themeInfo = serviceInfo.getThemeInfo();
-          LOG.info("Theme for stackName={}, stackVersion={}, serviceName={}, themeInfo={}", stackName, stackVersion, stackService, themeInfo);
-          if (themeInfo != null) {
-            Map<String, Object> themeMap = serviceInfo.getThemeInfo().getThemeMap();
-            if (themeMap != null) {
-              Resource resource = new ResourceImpl(Resource.Type.StackArtifact);
-              Set<String> requestedIds = getRequestPropertyIds(request, predicate);
-              setResourceProperty(resource, ARTIFACT_NAME_PROPERTY_ID, THEME_ARTIFACT_NAME, requestedIds);
-              setResourceProperty(resource, ARTIFACT_DATA_PROPERTY_ID, themeMap, requestedIds);
-              setResourceProperty(resource, STACK_NAME_PROPERTY_ID, stackName, requestedIds);
-              setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, stackVersion, requestedIds);
-              setResourceProperty(resource, STACK_SERVICE_NAME_PROPERTY_ID, serviceInfo.getName(), requestedIds);
-
-              resources.add(resource);
-            }
-          }
-
-        }
-      }
-    }
-    return resources;
-  }
-
   /**
    * Get all stack and stack service metrics descriptor resources.
    *

+ 203 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ThemeArtifactResourceProvider.java

@@ -0,0 +1,203 @@
+/*
+ * 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.controller.internal;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.AmbariManagementController;
+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 org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.ambari.server.state.StackInfo;
+import org.apache.ambari.server.state.ThemeInfo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ThemeArtifactResourceProvider extends AbstractControllerResourceProvider {
+
+  public static final String STACK_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "stack_name");
+  public static final String STACK_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "stack_version");
+  public static final String STACK_SERVICE_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "service_name");
+  public static final String THEME_FILE_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "file_name");
+  public static final String THEME_DEFAULT_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "default");
+  public static final String THEME_DATA_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "theme_data");
+
+  /**
+   * primary key fields
+   */
+  public static Set<String> pkPropertyIds = new HashSet<String>();
+  /**
+   * map of resource type to fk field
+   */
+  public static Map<Resource.Type, String> keyPropertyIds =
+    new HashMap<Resource.Type, String>();
+
+  /**
+   * resource properties
+   */
+  public static Set<String> propertyIds = new HashSet<String>();
+
+  static {
+    keyPropertyIds.put(Resource.Type.Theme, THEME_FILE_NAME_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.Stack, STACK_NAME_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.StackVersion, STACK_VERSION_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.StackService, STACK_SERVICE_NAME_PROPERTY_ID);
+
+    pkPropertyIds.add(THEME_FILE_NAME_PROPERTY_ID);
+
+    // resource properties
+    propertyIds.add(STACK_NAME_PROPERTY_ID);
+    propertyIds.add(STACK_VERSION_PROPERTY_ID);
+    propertyIds.add(STACK_SERVICE_NAME_PROPERTY_ID);
+    propertyIds.add(THEME_FILE_NAME_PROPERTY_ID);
+    propertyIds.add(THEME_DEFAULT_PROPERTY_ID);
+    propertyIds.add(THEME_DATA_PROPERTY_ID);
+  }
+
+  /**
+   * Create a  new resource provider for the given management controller.
+   *
+   * @param managementController the management controller
+   */
+  protected ThemeArtifactResourceProvider(AmbariManagementController managementController) {
+    super(propertyIds, keyPropertyIds, managementController);
+  }
+
+  @Override
+  public RequestStatus createResources(Request request) throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException,
+      NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Creating of themes is not supported");
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException,
+      NoSuchParentResourceException {
+
+    Set<Resource> resources = new LinkedHashSet<Resource>();
+
+    resources.addAll(getThemes(request, predicate));
+    // add other artifacts types here
+
+    if (resources.isEmpty()) {
+      throw new NoSuchResourceException(
+        "The requested resource doesn't exist: Themes not found, " + predicate);
+    }
+
+    return resources;
+
+  }
+
+  @Override
+  public RequestStatus updateResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException,
+      NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Updating of themes is not supported");
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Deleting of themes is not supported");
+  }
+
+  private Set<Resource> getThemes(Request request, Predicate predicate) throws NoSuchParentResourceException,
+    NoSuchResourceException, UnsupportedPropertyException, SystemException {
+
+    Set<Resource> resources = new LinkedHashSet<Resource>();
+    for (Map<String, Object> properties : getPropertyMaps(predicate)) {
+      String themeFileName = (String) properties.get(THEME_FILE_NAME_PROPERTY_ID);
+
+
+      String stackName = (String) properties.get(STACK_NAME_PROPERTY_ID);
+      String stackVersion = (String) properties.get(STACK_VERSION_PROPERTY_ID);
+      String stackService = (String) properties.get(STACK_SERVICE_NAME_PROPERTY_ID);
+
+      StackInfo stackInfo;
+      try {
+        stackInfo = getManagementController().getAmbariMetaInfo().getStack(stackName, stackVersion);
+      } catch (AmbariException e) {
+        throw new NoSuchParentResourceException(String.format(
+          "Parent stack resource doesn't exist: stackName='%s', stackVersion='%s'", stackName, stackVersion));
+      }
+
+      List<ServiceInfo> serviceInfoList = new ArrayList<ServiceInfo>();
+
+      if (stackService == null) {
+        serviceInfoList.addAll(stackInfo.getServices());
+      } else {
+        ServiceInfo service = stackInfo.getService(stackService);
+        if (service == null) {
+          throw new NoSuchParentResourceException(String.format(
+            "Parent stack/service resource doesn't exist: stackName='%s', stackVersion='%s', serviceName='%s'",
+            stackName, stackVersion, stackService));
+        }
+        serviceInfoList.add(service);
+      }
+
+      for (ServiceInfo serviceInfo : serviceInfoList) {
+        List<ThemeInfo> serviceThemes = new ArrayList<ThemeInfo>();
+        if (themeFileName != null) {
+          LOG.debug("Getting themes from service {}, themes = {}", serviceInfo.getName(), serviceInfo.getThemesMap());
+          serviceThemes.add(serviceInfo.getThemesMap().get(themeFileName));
+        } else {
+          for (ThemeInfo themeInfo : serviceInfo.getThemesMap().values()) {
+            if (themeInfo.getIsDefault()) {
+              serviceThemes.add(0, themeInfo);
+            } else {
+              serviceThemes.add(themeInfo);
+            }
+          }
+        }
+
+        List<Resource> serviceResources = new ArrayList<Resource>();
+        for (ThemeInfo themeInfo : serviceThemes) {
+          Resource resource = new ResourceImpl(Resource.Type.Theme);
+          Set<String> requestedIds = getRequestPropertyIds(request, predicate);
+          setResourceProperty(resource, THEME_FILE_NAME_PROPERTY_ID, themeInfo.getFileName(), requestedIds);
+          setResourceProperty(resource, THEME_DEFAULT_PROPERTY_ID, themeInfo.getIsDefault(), requestedIds);
+          setResourceProperty(resource, THEME_DATA_PROPERTY_ID, themeInfo.getThemeMap(), requestedIds);
+          setResourceProperty(resource, STACK_NAME_PROPERTY_ID, stackName, requestedIds);
+          setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, stackVersion, requestedIds);
+          setResourceProperty(resource, STACK_SERVICE_NAME_PROPERTY_ID, serviceInfo.getName(), requestedIds);
+          serviceResources.add(resource);
+        }
+
+        resources.addAll(serviceResources);
+
+      }
+    }
+    return resources;
+  }
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return null;
+  }
+}

+ 3 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java

@@ -137,7 +137,8 @@ public interface Resource {
     PreUpgradeCheck,
     Stage,
     StackArtifact,
-    Artifact;
+    Artifact,
+    Theme;
 
     /**
      * Get the {@link Type} that corresponds to this InternalType.
@@ -236,6 +237,7 @@ public interface Resource {
     public static final Type Stage = InternalType.Stage.getType();
     public static final Type StackArtifact = InternalType.StackArtifact.getType();
     public static final Type Artifact = InternalType.Artifact.getType();
+    public static final Type Theme = InternalType.Theme.getType();
 
     /**
      * The type name.

+ 3 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java

@@ -20,14 +20,17 @@ package org.apache.ambari.server.orm.entities;
 import java.util.Collections;
 import java.util.List;
 
+import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.FetchType;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.Lob;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.persistence.TableGenerator;
 import javax.persistence.UniqueConstraint;

+ 0 - 3
ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java

@@ -109,9 +109,6 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
         + File.separator + AmbariMetaInfo.KERBEROS_DESCRIPTOR_FILE_NAME);
     kerberosDescriptorFile = kdf.exists() ? kdf : null;
 
-    File themeFile = new File(directory.getAbsolutePath() + File.separator + AmbariMetaInfo.SERVICE_THEME_FILE_NAME);
-    this.themeFile = themeFile.exists() ? themeFile : null;
-
   }
 
   /**

+ 28 - 5
ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java

@@ -18,6 +18,7 @@
 
 package org.apache.ambari.server.stack;
 
+import java.io.File;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -32,6 +33,7 @@ import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.CustomCommandDefinition;
 import org.apache.ambari.server.state.PropertyInfo;
 import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.ambari.server.state.ThemeInfo;
 
 /**
  * Service module which provides all functionality related to parsing and fully
@@ -172,6 +174,9 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
     if (serviceInfo.getKerberosDescriptorFile() == null) {
       serviceInfo.setKerberosDescriptorFile(parent.getKerberosDescriptorFile());
     }
+    if (serviceInfo.getThemesMap().isEmpty()) {
+      serviceInfo.setThemesMap(parent.getThemesMap());
+    }
 
     mergeCustomCommands(parent.getCustomCommands(), serviceInfo.getCustomCommands());
     mergeConfigDependencies(parent);
@@ -285,11 +290,18 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
 
   private void populateThemeModules() {
 
-    ThemeModule themeModule = new ThemeModule(serviceDirectory.getThemeFile());
+    if (serviceInfo.getThemesDir() == null) {
+      serviceInfo.setThemesDir(AmbariMetaInfo.SERVICE_THEMES_FOLDER_NAME);
+    }
+
+    String themesDir = serviceDirectory.getAbsolutePath() + File.separator + serviceInfo.getThemesDir();
 
-    if (themeModule.isValid()) {
-      serviceInfo.setThemeInfo(themeModule.getModuleInfo());
-      themeModules.put(themeModule.getId(), themeModule);
+    if (serviceInfo.getThemes() != null) {
+      for (ThemeInfo themeInfo : serviceInfo.getThemes()) {
+        File themeFile = new File(themesDir + File.separator + themeInfo.getFileName());
+        ThemeModule module = new ThemeModule(themeFile, themeInfo);
+        themeModules.put(module.getId(), module);
+      }
     }
 
     //lets not fail if theme contain errors
@@ -300,7 +312,18 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
    */
   private void mergeThemes(ServiceModule parent, Map<String, StackModule> allStacks,
                            Map<String, ServiceModule> commonServices) throws AmbariException {
-    mergeChildModules(allStacks, commonServices, themeModules, parent.themeModules);
+    Collection<ThemeModule> mergedModules = mergeChildModules(allStacks, commonServices, themeModules, parent.themeModules);
+
+    for (ThemeModule mergedModule : mergedModules) {
+      themeModules.put(mergedModule.getId(), mergedModule);
+      ThemeInfo moduleInfo = mergedModule.getModuleInfo();
+      if (!moduleInfo.isDeleted()) {
+        serviceInfo.getThemesMap().put(moduleInfo.getFileName(), moduleInfo);
+      } else {
+        serviceInfo.getThemesMap().remove(moduleInfo.getFileName());
+      }
+
+    }
 
   }
 

+ 16 - 7
ambari-server/src/main/java/org/apache/ambari/server/stack/ThemeModule.java

@@ -43,14 +43,18 @@ public class ThemeModule extends BaseModule<ThemeModule, ThemeInfo> implements V
   }
 
 
-  private ThemeInfo moduleInfo = new ThemeInfo();
+  private ThemeInfo moduleInfo;
   private boolean valid = true;
   private Set<String> errors = new HashSet<String>();
 
   public ThemeModule(File themeFile) {
+    this(themeFile, new ThemeInfo());
+  }
 
-    if (themeFile == null) {
-    } else {
+  public ThemeModule(File themeFile, ThemeInfo moduleInfo) {
+    this.moduleInfo = moduleInfo;
+    if (!moduleInfo.isDeleted() && themeFile != null) {
+      LOG.debug("Looking for theme in {}", themeFile.getAbsolutePath());
       FileReader reader = null;
       try {
         reader = new FileReader(themeFile);
@@ -58,10 +62,11 @@ public class ThemeModule extends BaseModule<ThemeModule, ThemeInfo> implements V
         LOG.error("Theme file not found");
       }
       try {
-        TypeReference<HashMap<String,Object>> typeRef = new TypeReference<HashMap<String,Object>>() {};
+        TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {
+        };
         Map<String, Object> map = mapper.readValue(reader, typeRef);
         moduleInfo.setThemeMap(map);
-        LOG.info("Loaded theme: {}", moduleInfo);
+        LOG.debug("Loaded theme: {}", moduleInfo);
       } catch (IOException e) {
         LOG.error("Unable to parse theme file ", e);
         setValid(false);
@@ -76,7 +81,11 @@ public class ThemeModule extends BaseModule<ThemeModule, ThemeInfo> implements V
 
   @Override
   public void resolve(ThemeModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices) throws AmbariException {
-    if (parent.getModuleInfo() != null) {
+    ThemeInfo parentModuleInfo = parent.getModuleInfo();
+
+
+
+    if (parent.getModuleInfo() != null && !moduleInfo.isDeleted()) {
       moduleInfo.setThemeMap(mergedMap(parent.getModuleInfo().getThemeMap(), moduleInfo.getThemeMap()));
     }
   }
@@ -126,7 +135,7 @@ public class ThemeModule extends BaseModule<ThemeModule, ThemeInfo> implements V
 
   @Override
   public String getId() {
-    return "theme";
+    return moduleInfo.getFileName();
   }
 
   @Override

+ 50 - 17
ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java

@@ -105,11 +105,9 @@ public class ServiceInfo implements Validable{
     this.restartRequiredAfterChange = restartRequiredAfterChange;
   }
 
-  @XmlTransient
-  private ThemeInfo themeInfo = null;
-  
   @XmlTransient
   private File metricsFile = null;
+
   @XmlTransient
   private Map<String, Map<String, List<MetricDefinition>>> metrics = null;
   
@@ -169,6 +167,18 @@ public class ServiceInfo implements Validable{
   @XmlElement(name="configuration-dir")
   private String configDir = AmbariMetaInfo.SERVICE_CONFIG_FOLDER_NAME;
 
+  @JsonIgnore
+  @XmlElement(name = "themes-dir")
+  private String themesDir = AmbariMetaInfo.SERVICE_THEMES_FOLDER_NAME;
+
+  @JsonIgnore
+  @XmlElementWrapper(name = "themes")
+  @XmlElements(@XmlElement(name = "theme"))
+  private List<ThemeInfo> themes;
+
+  @XmlTransient
+  private volatile Map<String, ThemeInfo> themesMap;
+
 
   /**
    * Map of of os-specific details that is exposed (and initialised from list)
@@ -611,20 +621,6 @@ public class ServiceInfo implements Validable{
     return excludedConfigTypes;
   }
 
-  /**
-   * @return theme description map
-   */
-  public ThemeInfo getThemeInfo() {
-    return themeInfo;
-  }
-
-  /**
-   * @param themeInfo map with theme description
-   */
-  public void setThemeInfo(ThemeInfo themeInfo) {
-    this.themeInfo = themeInfo;
-  }
-
   public void setExcludedConfigTypes(Set<String> excludedConfigTypes) {
     this.excludedConfigTypes = excludedConfigTypes;
   }
@@ -666,4 +662,41 @@ public class ServiceInfo implements Validable{
   public void setRestartRequiredAfterRackChange(Boolean restartRequiredAfterRackChange) {
     this.restartRequiredAfterRackChange = restartRequiredAfterRackChange;
   }
+
+  public String getThemesDir() {
+    return themesDir;
+  }
+
+  public void setThemesDir(String themesDir) {
+    this.themesDir = themesDir;
+  }
+
+  public List<ThemeInfo> getThemes() {
+    return themes;
+  }
+
+  public void setThemes(List<ThemeInfo> themes) {
+    this.themes = themes;
+  }
+
+  public Map<String, ThemeInfo> getThemesMap() {
+    if (themesMap == null) {
+      synchronized (this) {
+      }
+      if (themesMap == null) {
+        Map<String, ThemeInfo> tmp = new TreeMap<String, ThemeInfo>();
+        if (themes != null) {
+          for (ThemeInfo theme : themes) {
+            tmp.put(theme.getFileName(), theme);
+          }
+        }
+        themesMap = tmp;
+      }
+    }
+    return themesMap;
+  }
+
+  public void setThemesMap(Map<String, ThemeInfo> themesMap) {
+    this.themesMap = themesMap;
+  }
 }

+ 40 - 1
ambari-server/src/main/java/org/apache/ambari/server/state/ThemeInfo.java

@@ -17,12 +17,24 @@
  */
 package org.apache.ambari.server.state;
 
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
 import java.util.Map;
 
 /**
  * Wrapper for theme description
  */
+@XmlAccessorType(XmlAccessType.FIELD)
 public class ThemeInfo {
+
+  private String fileName;
+  @XmlElement(name = "default")
+  private Boolean isDefault = false;
+  private Boolean deleted = false;
+
+  @XmlTransient
   private Map<String, Object> themeMap = null;
 
   public ThemeInfo() {
@@ -39,7 +51,34 @@ public class ThemeInfo {
   @Override
   public String toString() {
     return "ThemeInfo{" +
-      "themeMap=" + themeMap +
+      "deleted=" + deleted +
+      ", fileName='" + fileName + '\'' +
+      ", isDefault=" + isDefault +
+      ", themeMap=" + themeMap +
       '}';
   }
+
+  public String getFileName() {
+    return fileName;
+  }
+
+  public void setFileName(String fileName) {
+    this.fileName = fileName;
+  }
+
+  public Boolean getIsDefault() {
+    return isDefault;
+  }
+
+  public void setIsDefault(Boolean isDefault) {
+    this.isDefault = isDefault;
+  }
+
+  public Boolean isDeleted() {
+    return deleted;
+  }
+
+  public void setDeleted(Boolean deleted) {
+    this.deleted = deleted;
+  }
 }

+ 8 - 0
ambari-server/src/main/resources/stacks/HDP/2.2/services/YARN/metainfo.xml

@@ -57,6 +57,13 @@
           </packages>
         </osSpecific>
       </osSpecifics>
+
+      <themes>
+        <theme>
+          <fileName>theme.json</fileName>
+          <default>true</default>
+        </theme>
+      </themes>
     </service>
 
     <service>
@@ -82,6 +89,7 @@
         </osSpecific>
       </osSpecifics>
       <configuration-dir>configuration-mapred</configuration-dir>
+      <themes-dir>themes-mapred</themes-dir>
 
     </service>
 

+ 0 - 0
ambari-server/src/main/resources/stacks/HDP/2.2/services/YARN/theme.json → ambari-server/src/main/resources/stacks/HDP/2.2/services/YARN/themes/theme.json