| /* |
| * 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.stratos.autoscaler.applications.parser; |
| |
| import org.apache.amber.oauth2.common.exception.OAuthProblemException; |
| import org.apache.amber.oauth2.common.exception.OAuthSystemException; |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.stratos.autoscaler.applications.ApplicationUtils; |
| import org.apache.stratos.autoscaler.applications.ClusterInformation; |
| import org.apache.stratos.autoscaler.applications.MTClusterInformation; |
| import org.apache.stratos.autoscaler.applications.STClusterInformation; |
| import org.apache.stratos.autoscaler.applications.ApplicationHolder; |
| import org.apache.stratos.autoscaler.applications.payload.PayloadData; |
| import org.apache.stratos.autoscaler.applications.pojo.*; |
| import org.apache.stratos.autoscaler.client.IdentityApplicationManagementServiceClient; |
| import org.apache.stratos.autoscaler.client.OAuthAdminServiceClient; |
| import org.apache.stratos.autoscaler.exception.AutoScalerException; |
| import org.apache.stratos.autoscaler.exception.CartridgeGroupNotFoundException; |
| import org.apache.stratos.autoscaler.exception.application.ApplicationDefinitionException; |
| import org.apache.stratos.autoscaler.exception.CartridgeNotFoundException; |
| import org.apache.stratos.autoscaler.pojo.ServiceGroup; |
| import org.apache.stratos.autoscaler.pojo.policy.PolicyManager; |
| import org.apache.stratos.autoscaler.pojo.policy.autoscale.AutoscalePolicy; |
| import org.apache.stratos.autoscaler.registry.RegistryManager; |
| import org.apache.stratos.autoscaler.util.AutoscalerConstants; |
| import org.apache.stratos.autoscaler.util.AutoscalerUtil; |
| import org.apache.stratos.cloud.controller.stub.domain.Cartridge; |
| import org.apache.stratos.common.Properties; |
| import org.apache.stratos.common.client.CloudControllerServiceClient; |
| import org.apache.stratos.messaging.domain.application.*; |
| import org.wso2.carbon.identity.oauth.stub.OAuthAdminServiceException; |
| |
| import java.rmi.RemoteException; |
| import java.util.*; |
| |
| /** |
| * Default implementation of the Application Parser. One Application should be processed by one |
| * instance of the DefaultApplicationParser. |
| */ |
| public class DefaultApplicationParser implements ApplicationParser { |
| |
| public static final String ALIAS = "alias"; |
| public static final String CARTRIDGE_TYPE = "type"; |
| public static final String LOAD_BALANCER = "lb"; |
| private static final String METADATA_APPENDER = "-"; |
| private static Log log = LogFactory.getLog(DefaultApplicationParser.class); |
| |
| private List<ApplicationClusterContext> applicationClusterContexts; |
| private Map<String, Properties> aliasToProperties; |
| private String oauthToken; |
| |
| public DefaultApplicationParser() { |
| this.applicationClusterContexts = new ArrayList<ApplicationClusterContext>(); |
| this.setAliasToProperties(new HashMap<String, Properties>()); |
| |
| } |
| |
| /** |
| * Validates terminationBehavior. The terminationBehavior should be one of the following: |
| * 1. terminate-none |
| * 2. terminate-dependents |
| * 3. terminate-all |
| * |
| * @throws ApplicationDefinitionException if terminationBehavior is different to what is |
| * listed above |
| */ |
| private static void validateTerminationBehavior(String terminationBehavior) throws ApplicationDefinitionException { |
| |
| if (!(terminationBehavior == null || |
| AutoscalerConstants.TERMINATE_NONE.equals(terminationBehavior) || |
| AutoscalerConstants.TERMINATE_DEPENDENTS.equals(terminationBehavior) || |
| AutoscalerConstants.TERMINATE_ALL.equals(terminationBehavior))) { |
| throw new ApplicationDefinitionException("Invalid termination behaviour found: [ " + |
| terminationBehavior + " ], should be one of '" + |
| AutoscalerConstants.TERMINATE_NONE + "', '" + |
| AutoscalerConstants.TERMINATE_DEPENDENTS + "', '" + |
| AutoscalerConstants.TERMINATE_ALL + "'"); |
| } |
| } |
| |
| @Override |
| public Application parse(ApplicationContext applicationContext) |
| throws ApplicationDefinitionException, CartridgeGroupNotFoundException, |
| CartridgeNotFoundException { |
| |
| if (applicationContext == null) { |
| handleError("Invalid application definition, application context is null"); |
| } |
| |
| assert applicationContext != null; |
| if (applicationContext.getAlias() == null || applicationContext.getAlias().isEmpty()) { |
| handleError("Invalid application definition, application alias is not found in application context"); |
| } |
| |
| if (applicationContext.getApplicationId() == null || applicationContext.getApplicationId().isEmpty()) { |
| handleError("Invalid application definition, application id is not found in application context"); |
| } |
| |
| // get the Subscribables Information |
| Map<String, SubscribableInfoContext> subscribablesInfo = getSubscribableInformation(applicationContext); |
| if (log.isDebugEnabled()) { |
| Set<Map.Entry<String, SubscribableInfoContext>> subscribableInfoCtxtEntries = subscribablesInfo.entrySet(); |
| log.debug("Defined Subscribable Information: [ "); |
| for (Map.Entry<String, SubscribableInfoContext> subscribableInfoCtxtEntry : subscribableInfoCtxtEntries) { |
| log.debug("Subscribable Information alias: " + subscribableInfoCtxtEntry.getKey()); |
| } |
| log.debug(" ]"); |
| } |
| |
| if (subscribablesInfo == null) { |
| handleError("Invalid application definition, subscribable information is not found in application context"); |
| } |
| |
| oauthToken = createToken(applicationContext.getApplicationId()); |
| return buildCompositeAppStructure(applicationContext, subscribablesInfo); |
| } |
| |
| @Override |
| public List<ApplicationClusterContext> getApplicationClusterContexts() throws ApplicationDefinitionException { |
| return applicationClusterContexts; |
| } |
| |
| /** |
| * Find subscribable information in cartridge contexts |
| * |
| * @param applicationId TODO |
| * @param subscribableInfoContextMap Map to return subscribable information |
| * @param cartridgeContexts Application to read cartridge contexts |
| * @return |
| * @throws ApplicationDefinitionException |
| */ |
| private void findSubscribableInfoOfCartridgeContexts(String applicationId, |
| Map<String, SubscribableInfoContext> subscribableInfoContextMap, CartridgeContext[] cartridgeContexts) |
| throws ApplicationDefinitionException { |
| |
| if (cartridgeContexts != null) { |
| for (CartridgeContext cartridgeContext : cartridgeContexts) { |
| if (cartridgeContext != null) { |
| SubscribableInfoContext subscribableInfoContext = cartridgeContext.getSubscribableInfoContext(); |
| addSubscribableInfo(applicationId, cartridgeContext.getType(), subscribableInfoContextMap, subscribableInfoContext); |
| } |
| } |
| } |
| } |
| |
| private void addSubscribableInfo(String applicationId, String cartridgeType, |
| Map<String, SubscribableInfoContext> subscribableInfoContextMap, |
| SubscribableInfoContext subscribableInfoContext) |
| throws ApplicationDefinitionException { |
| |
| String alias = subscribableInfoContext.getAlias(); |
| String autoscalingPolicyId = subscribableInfoContext.getAutoscalingPolicy(); |
| |
| if (StringUtils.isEmpty(alias)) { |
| handleError(String.format("An alias is not defined for cartridge: [application-id] %s" + |
| "[cartridge-type] %s", applicationId, cartridgeType)); |
| } |
| |
| if (!ApplicationUtils.isAliasValid(alias)) { |
| handleError(String.format("Alias is not valid: [application-id] %s " + |
| "[cartridge-type] %s [alias] %s [valid-pattern] %s", applicationId, cartridgeType, alias, |
| ApplicationUtils.ALIAS_PATTERN.pattern())); |
| } |
| |
| if (subscribableInfoContextMap.get(alias) != null) { |
| handleError(String.format("Alias is already defined: [application-id] %s " + |
| "[cartridge-type] %s [alias] %s", applicationId, cartridgeType, alias)); |
| } |
| |
| if (StringUtils.isBlank(autoscalingPolicyId)) { |
| handleError(String.format("Autoscaling policy is not defined: [application-id] %s " + |
| "[cartridge-type] %s [alias] %s", applicationId, cartridgeType, alias)); |
| } |
| |
| AutoscalePolicy autoscalingPolicy = PolicyManager.getInstance().getAutoscalePolicy(autoscalingPolicyId); |
| if (autoscalingPolicy == null) { |
| handleError(String.format("Autoscaling policy is not found: [application-id] %s " + |
| "[cartridge-type] %s [alias] %s [autoscaling-policy] %s", applicationId, cartridgeType, |
| alias, autoscalingPolicyId)); |
| } |
| |
| subscribableInfoContextMap.put(alias, subscribableInfoContext); |
| if (log.isDebugEnabled()) { |
| log.debug(String.format("Subscribable information added: [application-id] %s " + |
| "[cartridge-type] %s [alias] %s", applicationId, cartridgeType, alias)); |
| } |
| } |
| |
| /** |
| * Get subscribable information of group contexts recursively. |
| * |
| * @param subscribableInfoContextMap Map to return subscribable information |
| * @param groupContexts Group contexts to read subscribable information |
| * @return |
| * @throws ApplicationDefinitionException |
| */ |
| private void findSubscribableInfoOfGroupContexts(String applicationId, |
| Map<String, SubscribableInfoContext> subscribableInfoContextMap, GroupContext[] groupContexts) |
| throws ApplicationDefinitionException { |
| |
| if (groupContexts != null) { |
| for (GroupContext groupContext : groupContexts) { |
| // finding SubscribableInfo in group |
| if (groupContext.getGroupContexts() != null) { |
| findSubscribableInfoOfGroupContexts(applicationId, subscribableInfoContextMap, groupContext.getGroupContexts()); |
| } |
| // finding SubscribableInfo in cartridge |
| if (groupContext.getCartridgeContexts() != null) { |
| findSubscribableInfoOfCartridgeContexts(applicationId, subscribableInfoContextMap, groupContext.getCartridgeContexts()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Extract Subscription Information from the Application Definition |
| * |
| * @param applicationContext ApplicationContext object with Application information |
| * @return Map [cartridge alias -> Group] |
| * @throws ApplicationDefinitionException if the Subscription information is invalid |
| */ |
| private Map<String, SubscribableInfoContext> getSubscribableInformation(ApplicationContext applicationContext) throws |
| ApplicationDefinitionException { |
| |
| Map<String, SubscribableInfoContext> subscribableInfoContextMap = new HashMap<String, SubscribableInfoContext>(); |
| |
| String applicationId = applicationContext.getApplicationId(); |
| ComponentContext componentContext = applicationContext.getComponents(); |
| if (componentContext != null) { |
| CartridgeContext[] cartridgeContexts = componentContext.getCartridgeContexts(); |
| if (cartridgeContexts != null) { |
| findSubscribableInfoOfCartridgeContexts(applicationId, subscribableInfoContextMap, cartridgeContexts); |
| } |
| GroupContext[] groupContexts = componentContext.getGroupContexts(); |
| if (groupContexts != null) { |
| findSubscribableInfoOfGroupContexts(applicationId, subscribableInfoContextMap, groupContexts); |
| } |
| } |
| |
| return subscribableInfoContextMap; |
| } |
| |
| /** |
| * Builds the Application structure |
| * |
| * @param applicationContext ApplicationContext object with Application information |
| * @param subscribableInfoCtxts Map [cartridge alias -> Group] with extracted Subscription Information |
| * @return Application Application object denoting the Application structure |
| * @throws ApplicationDefinitionException If an error occurs in building the Application structure |
| */ |
| private Application buildCompositeAppStructure(ApplicationContext applicationContext, |
| Map<String, SubscribableInfoContext> subscribableInfoCtxts) |
| throws ApplicationDefinitionException, CartridgeGroupNotFoundException, CartridgeNotFoundException { |
| |
| Application application; |
| |
| // check if application already exists, and get existing key if true |
| ApplicationHolder.acquireReadLock(); |
| Applications persistedApplications = ApplicationHolder.getApplications(); |
| if (persistedApplications != null && persistedApplications.applicationExists(applicationContext.getApplicationId())) { |
| String existingKey = persistedApplications.getApplication(applicationContext.getApplicationId()).getKey(); |
| application = new Application(applicationContext.getApplicationId(), existingKey); |
| } else { |
| application = new Application(applicationContext.getApplicationId()); |
| } |
| |
| ApplicationHolder.releaseReadLock(); |
| |
| // Set tenant information |
| application.setTenantId(applicationContext.getTenantId()); |
| application.setTenantDomain(applicationContext.getTenantDomain()); |
| application.setTenantAdminUserName(applicationContext.getTenantAdminUsername()); |
| |
| Set<StartupOrder> startupOrderSet = new LinkedHashSet<StartupOrder>(); |
| DependencyOrder dependencyOrder = new DependencyOrder(); |
| dependencyOrder.setStartupOrders(startupOrderSet); |
| application.setDependencyOrder(dependencyOrder); |
| |
| ComponentContext components = applicationContext.getComponents(); |
| if (components != null) { |
| DependencyContext dependencyContext = components.getDependencyContext(); |
| |
| // Set top level dependencies |
| if (dependencyContext != null) { |
| // Set startup orders |
| String[] startupOrders = dependencyContext.getStartupOrdersContexts(); |
| if (startupOrders != null) { |
| if (log.isDebugEnabled()) { |
| log.debug("Parsing application: startupOrders != null for app alias: " + |
| applicationContext.getAlias() + " #: " + startupOrders.length); |
| } |
| // validate alias |
| ParserUtils.validateStartupOrderAlias(startupOrders, applicationContext); |
| dependencyOrder.setStartupOrders(ParserUtils.convertStartupOrder(startupOrders)); |
| } else { |
| if (log.isDebugEnabled()) { |
| log.debug("Parsing application: startupOrders == null for app alias: " + |
| applicationContext.getAlias()); |
| } |
| } |
| |
| // Set scaling dependents |
| String[] scalingDependents = dependencyContext.getScalingDependents(); |
| if (scalingDependents != null) { |
| if (log.isDebugEnabled()) { |
| log.debug("Parsing application: scalingDependents != null for app alias: " + |
| applicationContext.getAlias() + " #: " + scalingDependents.length); |
| } |
| dependencyOrder.setScalingDependents(ParserUtils.convertScalingDependentList(scalingDependents)); |
| } else { |
| if (log.isDebugEnabled()) { |
| log.debug("Parsing application: scalingDependents == null for app alias: " + |
| applicationContext.getAlias()); |
| } |
| } |
| |
| // Set termination behavior |
| String terminationBehaviour = dependencyContext.getTerminationBehaviour(); |
| validateTerminationBehavior(terminationBehaviour); |
| dependencyOrder.setTerminationBehaviour(terminationBehaviour); |
| } |
| |
| // Set application cluster data |
| CartridgeContext[] cartridgeContexts = components.getCartridgeContexts(); |
| if (cartridgeContexts != null) { |
| List<CartridgeContext> cartridgeContextList = Arrays.asList(cartridgeContexts); |
| Set<StartupOrder> startupOrders = application.getDependencyOrder().getStartupOrders(); |
| Map<String, Map<String, ClusterDataHolder>> clusterDataMap; |
| |
| clusterDataMap = parseLeafLevelSubscriptions(applicationContext.getApplicationId(), |
| applicationContext.getTenantId(), application.getKey(), null, cartridgeContextList, startupOrders); |
| application.setClusterData(clusterDataMap.get(ALIAS)); |
| application.setClusterDataForType(clusterDataMap.get(CARTRIDGE_TYPE)); |
| } |
| |
| // Set groups |
| if (components.getGroupContexts() != null) { |
| application.setGroups( |
| parseGroups(applicationContext.getApplicationId(), applicationContext.getTenantId(), |
| application.getKey(), Arrays.asList(components.getGroupContexts()), |
| subscribableInfoCtxts)); |
| } |
| } |
| |
| // setting alias to deployment policy id map to the application |
| application.setAliasToDeploymentPolicyIdMap( |
| AutoscalerUtil.getAliasToDeploymentPolicyIdMapOfApplication(applicationContext)); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Application parsed successfully: [application-id] " + applicationContext.getApplicationId()); |
| } |
| return application; |
| } |
| |
| |
| /** |
| * Parse Subscription Information |
| * |
| * @param appId Application id |
| * @param tenantId Tenant id of tenant which deployed the Application |
| * @param key Generated key for the Application |
| * @param groupName Group name (if relevant) |
| * @param cartridgeContextList cartridgeContextList |
| * @return Map [subscription alias -> ClusterDataHolder] |
| * @throws ApplicationDefinitionException |
| */ |
| private Map<String, Map<String, ClusterDataHolder>> parseLeafLevelSubscriptions( |
| String appId, int tenantId, String key, String groupName, |
| List<CartridgeContext> cartridgeContextList, Set<StartupOrder> dependencyOrder) throws ApplicationDefinitionException, CartridgeNotFoundException { |
| |
| Map<String, Map<String, ClusterDataHolder>> completeDataHolder = new HashMap<String, Map<String, ClusterDataHolder>>(); |
| Map<String, ClusterDataHolder> clusterDataMap = new HashMap<String, ClusterDataHolder>(); |
| Map<String, ClusterDataHolder> clusterDataMapByType = new HashMap<String, ClusterDataHolder>(); |
| |
| createClusterDataMap(appId, cartridgeContextList, clusterDataMap, clusterDataMapByType); |
| |
| for (CartridgeContext cartridgeContext : cartridgeContextList) { |
| List<String> dependencyClusterIDs = new ArrayList<String>(); |
| List<String> exportMetadataKeys = new ArrayList<String>(); |
| List<String> importMetadataKeys = new ArrayList<String>(); |
| String cartridgeType = cartridgeContext.getType(); |
| SubscribableInfoContext subscribableInfoContext = cartridgeContext.getSubscribableInfoContext(); |
| String subscriptionAlias = subscribableInfoContext.getAlias(); |
| |
| Cartridge cartridge = getCartridge(cartridgeType); |
| if (cartridge == null) { |
| throw new CartridgeNotFoundException("Cartridge not found " + cartridgeType); |
| } |
| |
| // Add metadata keys defined in cartridges as export metadata keys |
| String[] metadataKeys = cartridge.getMetadataKeys(); |
| if (metadataKeys != null) { |
| for (String str : metadataKeys) { |
| if (!StringUtils.isBlank(str)) { |
| exportMetadataKeys.add(cartridgeContext.getSubscribableInfoContext() |
| .getAlias() + METADATA_APPENDER + str); |
| } |
| } |
| } |
| |
| // get hostname and cluster id |
| ClusterInformation clusterInfo; |
| if (cartridge.getMultiTenant()) { |
| clusterInfo = new MTClusterInformation(); |
| } else { |
| clusterInfo = new STClusterInformation(); |
| } |
| |
| String hostname = clusterInfo.getHostName(appId, subscriptionAlias, cartridge.getHostName()); |
| String clusterId = clusterInfo.getClusterId(appId, subscriptionAlias, cartridgeType); |
| String repoUrl = null; |
| if (subscribableInfoContext.getArtifactRepositoryContext() != null) { |
| repoUrl = subscribableInfoContext.getArtifactRepositoryContext().getRepoUrl(); |
| } |
| |
| // Find import metadata keys |
| if (dependencyOrder != null) { |
| for (StartupOrder startupOrder : dependencyOrder) { |
| for (String startupOrderComponent : startupOrder.getStartupOrderComponentList()) { |
| |
| String[] arrStartUp = startupOrderComponent.split("\\."); |
| if (arrStartUp[0].equals("cartridge")) { |
| String cartridgeAlias = arrStartUp[1]; |
| String dependentCartridgeType = |
| findCartridgeTypeFromAlias(cartridgeContextList, cartridgeAlias); |
| if (StringUtils.isBlank(dependentCartridgeType)) { |
| throw new CartridgeNotFoundException( |
| String.format("Could not find dependent cartridge for " + |
| "application: %s cartridge-alias: %s", appId, cartridgeAlias)); |
| } |
| Cartridge dependencyCartridge = getCartridge(dependentCartridgeType); |
| ClusterDataHolder dataHolder = clusterDataMapByType.get(dependentCartridgeType); |
| |
| if (dataHolder != null) { |
| if (!dataHolder.getClusterId().equals(clusterId)) { |
| dependencyClusterIDs.add(dataHolder.getClusterId()); |
| if (dependencyCartridge.getMetadataKeys() != null) { |
| for (String str : dependencyCartridge.getMetadataKeys()) { |
| if (!StringUtils.isBlank(str)) { |
| importMetadataKeys.add(dataHolder.getClusterId().split("\\.")[0] + |
| METADATA_APPENDER + str); |
| } |
| } |
| } |
| if (!dataHolder.getClusterId().equals(clusterId)) { |
| if (startupOrderComponent.equals("cartridge.".concat(cartridgeType))) { |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| String[] arrDependencyClusterIDs = new String[dependencyClusterIDs.size()]; |
| arrDependencyClusterIDs = dependencyClusterIDs.toArray(arrDependencyClusterIDs); |
| |
| String[] arrExportMetadata = new String[exportMetadataKeys.size()]; |
| arrExportMetadata = exportMetadataKeys.toArray(arrExportMetadata); |
| String[] arrImportMetadata = new String[importMetadataKeys.size()]; |
| arrImportMetadata = importMetadataKeys.toArray(arrImportMetadata); |
| |
| // Find tenant range of cluster |
| String tenantRange = AutoscalerUtil.findTenantRange(tenantId, cartridge.getTenantPartitions()); |
| Boolean isLB = false; |
| if (cartridge.getCategory().equals(LOAD_BALANCER)) { |
| isLB = true; |
| } |
| // create and collect this cluster's information |
| ApplicationClusterContext appClusterCtxt = createApplicationClusterContext(appId, groupName, cartridge, |
| key, tenantId, repoUrl, subscriptionAlias, clusterId, hostname, |
| subscribableInfoContext.getDeploymentPolicy(), isLB, |
| tenantRange, subscribableInfoContext.getDependencyAliases(), |
| subscribableInfoContext.getProperties(), arrDependencyClusterIDs, arrExportMetadata, |
| arrImportMetadata, subscribableInfoContext.getLvsVirtualIP()); |
| |
| appClusterCtxt.setAutoscalePolicyName(subscribableInfoContext.getAutoscalingPolicy()); |
| appClusterCtxt.setProperties(subscribableInfoContext.getProperties()); |
| if (subscribableInfoContext.getPersistenceContext() != null) { |
| appClusterCtxt.setPersistenceContext(subscribableInfoContext.getPersistenceContext()); |
| } |
| this.applicationClusterContexts.add(appClusterCtxt); |
| |
| |
| } |
| completeDataHolder.put(CARTRIDGE_TYPE, clusterDataMapByType); |
| completeDataHolder.put(ALIAS, clusterDataMap); |
| return completeDataHolder; |
| } |
| |
| /** |
| * Find alias of a cartridge by cartridge type |
| * |
| * @param cartridgeContextList |
| * @param alias |
| * @return |
| */ |
| private String findCartridgeTypeFromAlias(List<CartridgeContext> cartridgeContextList, String alias) { |
| for (CartridgeContext cartridgeContext : cartridgeContextList) { |
| if (alias.equals(cartridgeContext.getSubscribableInfoContext().getAlias())) { |
| return cartridgeContext.getType(); |
| } |
| } |
| return null; |
| } |
| |
| private void createClusterDataMap(String applicationId, |
| List<CartridgeContext> cartridgeContextList, |
| Map<String, ClusterDataHolder> clusterDataMap, Map<String, ClusterDataHolder> clusterDataMapByType) |
| throws ApplicationDefinitionException { |
| for (CartridgeContext cartridgeContext : cartridgeContextList) { |
| |
| String cartridgeType = cartridgeContext.getType(); |
| SubscribableInfoContext subscribableInfoContext = cartridgeContext.getSubscribableInfoContext(); |
| String subscriptionAlias = subscribableInfoContext.getAlias(); |
| |
| // check if a cartridge with relevant type is already deployed. else, can't continue |
| Cartridge cartridge = getCartridge(cartridgeType); |
| if (cartridge == null) { |
| handleError("No deployed Cartridge found with type [ " + cartridgeType + |
| " ] for Composite Application"); |
| } |
| |
| // get hostname and cluster id |
| ClusterInformation clusterInfo; |
| assert cartridge != null; |
| if (cartridge.getMultiTenant()) { |
| clusterInfo = new MTClusterInformation(); |
| } else { |
| clusterInfo = new STClusterInformation(); |
| } |
| |
| String clusterId = clusterInfo.getClusterId(applicationId, subscriptionAlias, cartridgeType); |
| // add relevant information to the map |
| ClusterDataHolder clusterDataHolderPerType = new ClusterDataHolder(cartridgeType, clusterId); |
| clusterDataHolderPerType.setMinInstances(cartridgeContext.getCartridgeMin()); |
| clusterDataHolderPerType.setMaxInstances(cartridgeContext.getCartridgeMax()); |
| clusterDataMapByType.put(cartridgeType, clusterDataHolderPerType); |
| // add relevant information to the map |
| ClusterDataHolder clusterDataHolder = new ClusterDataHolder(cartridgeType, clusterId); |
| clusterDataHolder.setMinInstances(cartridgeContext.getCartridgeMin()); |
| clusterDataHolder.setMaxInstances(cartridgeContext.getCartridgeMax()); |
| clusterDataMap.put(subscriptionAlias, clusterDataHolder); |
| |
| } |
| } |
| |
| /** |
| * Parse Group information |
| * |
| * @param appId Application id |
| * @param tenantId tenant id of tenant which deployed the Application |
| * @param key Generated key for the Application |
| * @param groupCtxts Group information |
| * @param subscribableInformation Subscribable Information |
| * @return Map [alias -> Group] |
| * @throws ApplicationDefinitionException if an error occurs in parsing Group Information |
| */ |
| private Map<String, Group> parseGroups(String appId, int tenantId, String key, List<GroupContext> groupCtxts, |
| Map<String, SubscribableInfoContext> subscribableInformation) |
| throws ApplicationDefinitionException, CartridgeGroupNotFoundException, CartridgeNotFoundException { |
| |
| Map<String, Group> groupAliasToGroup = new HashMap<String, Group>(); |
| |
| for (GroupContext groupCtxt : groupCtxts) { |
| ServiceGroup serviceGroup = getServiceGroup(groupCtxt.getName()); |
| if (serviceGroup == null) { |
| log.error("Cartridge group not found group-name: " + groupCtxt.getName()); |
| throw new CartridgeGroupNotFoundException("Cartridge group not found group-name: " |
| + groupCtxt.getName()); |
| } |
| Group group = parseGroup(appId, tenantId, key, groupCtxt, subscribableInformation, serviceGroup); |
| validateCartridgeGroupReference(appId, serviceGroup, group); |
| groupAliasToGroup.put(group.getAlias(), group); |
| } |
| |
| Set<Group> nestedGroups = new HashSet<Group>(); |
| getNestedGroupContexts(nestedGroups, groupAliasToGroup.values()); |
| filterDuplicatedGroupContexts(groupAliasToGroup.values(), nestedGroups); |
| |
| return groupAliasToGroup; |
| } |
| |
| /** |
| * Validate cartridge group reference against cartridge group definition |
| * |
| * @param serviceGroup |
| * @param group |
| */ |
| private void validateCartridgeGroupReference(String applicationId, |
| ServiceGroup serviceGroup, Group group) |
| throws CartridgeNotFoundException { |
| List<String> cartridgeTypes = findCartridgeTypesInServiceGroup(serviceGroup); |
| for (String cartridgeType : cartridgeTypes) { |
| if (findClusterDataInGroup(group, cartridgeType) == null) { |
| log.error(String.format("Cartridge %s not defined in cartridge group: " + |
| "[application] %s [cartridge-group-name] %s [cartridge-group-alias] %s", |
| cartridgeType, applicationId, group.getName(), group.getAlias())); |
| throw new CartridgeNotFoundException(String.format("Cartridge %s not defined in cartridge group: " + |
| "[application] %s [cartridge-group-name] %s [cartridge-group-alias] %s", |
| cartridgeType, applicationId, group.getName(), group.getAlias())); |
| } |
| } |
| } |
| |
| /** |
| * Find cluster data in a group recursively by cartridge type. |
| * |
| * @param group |
| * @param cartridgeType |
| * @return |
| */ |
| private ClusterDataHolder findClusterDataInGroup(Group group, String cartridgeType) { |
| Map<String, ClusterDataHolder> clusterDataForType = group.getClusterDataForType(); |
| if (clusterDataForType != null) { |
| ClusterDataHolder clusterData = clusterDataForType.get(cartridgeType); |
| if (clusterData != null) { |
| return clusterData; |
| } |
| |
| if (group.getGroups() != null) { |
| for (Group childGroup : group.getGroups()) { |
| ClusterDataHolder dataHolder = findClusterDataInGroup(childGroup, cartridgeType); |
| if (dataHolder != null) { |
| return dataHolder; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Find cartridge types available in a service group recursively. |
| * |
| * @param serviceGroup |
| * @return |
| */ |
| private List<String> findCartridgeTypesInServiceGroup(ServiceGroup serviceGroup) { |
| List<String> cartridgeTypes = new ArrayList<String>(); |
| if (serviceGroup.getCartridges() != null) { |
| for (String cartridgeType : serviceGroup.getCartridges()) { |
| cartridgeTypes.add(cartridgeType); |
| } |
| } |
| if (serviceGroup.getGroups() != null) { |
| for (ServiceGroup childServiceGroup : serviceGroup.getGroups()) { |
| List<String> childCartridgeTypes = findCartridgeTypesInServiceGroup(childServiceGroup); |
| for (String cartridgeType : childCartridgeTypes) { |
| cartridgeTypes.add(cartridgeType); |
| } |
| } |
| } |
| return cartridgeTypes; |
| } |
| |
| /** |
| * Extracts nested Group information recursively |
| * |
| * @param nestedGroups Nested Groups set to be populated recursively |
| * @param groups Collection of Groups |
| */ |
| private void getNestedGroupContexts(Set<Group> nestedGroups, Collection<Group> groups) { |
| |
| if (groups != null) { |
| for (Group group : groups) { |
| if (group.getGroups() != null) { |
| nestedGroups.addAll(group.getGroups()); |
| getNestedGroupContexts(nestedGroups, group.getGroups()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Filters duplicated Groups from top level |
| * |
| * @param topLevelGroups Top level Groups |
| * @param nestedGroups nested Groups |
| */ |
| private void filterDuplicatedGroupContexts(Collection<Group> topLevelGroups, Set<Group> nestedGroups) { |
| |
| for (Group nestedGroup : nestedGroups) { |
| filterNestedGroupFromTopLevel(topLevelGroups, nestedGroup); |
| } |
| } |
| |
| private void filterNestedGroupFromTopLevel(Collection<Group> topLevelGroups, Group nestedGroup) { |
| |
| Iterator<Group> parentIterator = topLevelGroups.iterator(); |
| while (parentIterator.hasNext()) { |
| Group parentGroup = parentIterator.next(); |
| // if there is an exactly similar nested Group Context and a top level Group Context |
| // it implies that they are duplicates. Should be removed from top level. |
| if (parentGroup.equals(nestedGroup)) { |
| parentIterator.remove(); |
| } |
| } |
| } |
| |
| /** |
| * Parses an individual Group |
| * |
| * @param appId Application id |
| * @param tenantId tenant id of tenant which deployed the Application |
| * @param key Generated key for the Application |
| * @param groupCtxt Group definition information |
| * @param subscribableInfoCtxts Map [cartridge alias -> Group] with extracted Subscription Information |
| * @return Group object |
| * @throws ApplicationDefinitionException if unable to parse |
| */ |
| private Group parseGroup(String appId, int tenantId, String key, GroupContext groupCtxt, |
| Map<String, SubscribableInfoContext> subscribableInfoCtxts, |
| ServiceGroup serviceGroup) |
| throws ApplicationDefinitionException, CartridgeNotFoundException { |
| |
| Group group = new Group(appId, groupCtxt.getName(), groupCtxt.getAlias()); |
| group.setGroupScalingEnabled(groupCtxt.getGroupMaxInstances() > 1); |
| group.setGroupMinInstances(groupCtxt.getGroupMinInstances()); |
| group.setGroupMaxInstances(groupCtxt.getGroupMaxInstances()); |
| |
| DependencyOrder dependencyOrder = new DependencyOrder(); |
| // create the Dependency Ordering |
| String[] startupOrders = getStartupOrderForGroup(groupCtxt.getName(), serviceGroup); |
| Set<StartupOrder> setStartUpOrder = null; |
| if (startupOrders != null) { |
| setStartUpOrder = ParserUtils.convertStartupOrder(startupOrders, groupCtxt); |
| dependencyOrder.setStartupOrders(setStartUpOrder); |
| } else { |
| if (log.isDebugEnabled()) { |
| String msg = String.format("No start up order defined [group-alias] %s", groupCtxt.getAlias()); |
| log.debug(msg); |
| } |
| } |
| |
| String[] scaleDependents = getScaleDependentForGroup(groupCtxt.getName(), serviceGroup); |
| if (scaleDependents != null) { |
| dependencyOrder.setScalingDependents(ParserUtils.convertScalingDependentList(scaleDependents, groupCtxt)); |
| } else { |
| if (log.isDebugEnabled()) { |
| String msg = String.format("No scaling dependent defined [group-alias] %s", groupCtxt.getAlias()); |
| log.debug(msg); |
| } |
| } |
| |
| dependencyOrder.setTerminationBehaviour(getTerminationBehaviour(groupCtxt.getName(), serviceGroup)); |
| group.setDependencyOrder(dependencyOrder); |
| |
| Map<String, Map<String, ClusterDataHolder>> clusterDataMap; |
| |
| // get group level CartridgeContexts |
| if (groupCtxt.getCartridgeContexts() != null) { |
| clusterDataMap = parseLeafLevelSubscriptions(appId, tenantId, key, groupCtxt.getName(), |
| Arrays.asList(groupCtxt.getCartridgeContexts()), setStartUpOrder); |
| group.setClusterData(clusterDataMap.get(ALIAS)); |
| group.setClusterDataForType(clusterDataMap.get(CARTRIDGE_TYPE)); |
| |
| } |
| |
| // get nested groups |
| if (groupCtxt.getGroupContexts() != null) { |
| Map<String, Group> nestedGroups = new HashMap<String, Group>(); |
| // check sub groups |
| for (GroupContext subGroupCtxt : groupCtxt.getGroupContexts()) { |
| // get the complete Group Definition |
| if (subGroupCtxt != null) { |
| for (ServiceGroup nestedServiceGroup : serviceGroup.getGroups()) { |
| if (nestedServiceGroup.getName().equals(subGroupCtxt.getName())) { |
| Group nestedGroup = parseGroup(appId, tenantId, key, |
| subGroupCtxt, subscribableInfoCtxts, |
| nestedServiceGroup); |
| nestedGroups.put(nestedGroup.getAlias(), nestedGroup); |
| } |
| } |
| |
| } |
| } |
| group.setGroups(nestedGroups); |
| } |
| |
| return group; |
| } |
| |
| /** |
| * Find the startup order |
| * |
| * @param serviceGroup GroupContext with Group defintion information |
| * @return Set of Startup Orders which are defined in the Group |
| * @throws ApplicationDefinitionException |
| */ |
| private String[] getStartupOrderForGroup(String serviceGroupName, ServiceGroup serviceGroup) throws ApplicationDefinitionException { |
| |
| ServiceGroup nestedServiceGroup = getNestedServiceGroup(serviceGroupName, serviceGroup); |
| |
| if (nestedServiceGroup == null) { |
| handleError("Service Group Definition not found for name " + serviceGroupName); |
| } |
| |
| if (log.isDebugEnabled()) { |
| log.debug("parsing application ... getStartupOrderForGroup: " + serviceGroupName); |
| } |
| |
| assert nestedServiceGroup != null; |
| if (nestedServiceGroup.getDependencies() != null) { |
| if (log.isDebugEnabled()) { |
| log.debug("parsing application ... getStartupOrderForGroup: dependencies != null "); |
| } |
| if (nestedServiceGroup.getDependencies().getStartupOrders() != null) { |
| |
| String[] startupOrders = nestedServiceGroup.getDependencies().getStartupOrders(); |
| if (log.isDebugEnabled()) { |
| log.debug("parsing application ... getStartupOrderForGroup: startupOrders != null # of: " + startupOrders.length); |
| } |
| return startupOrders; |
| } |
| } |
| |
| return null; |
| } |
| |
| |
| /** |
| * Find the scale dependent order |
| * |
| * @param serviceGroup GroupContext with Group defintion information |
| * @return Set of Startup Orders which are defined in the Group |
| * @throws ApplicationDefinitionException |
| */ |
| private String[] getScaleDependentForGroup(String serviceGroupName, ServiceGroup serviceGroup) throws ApplicationDefinitionException { |
| |
| ServiceGroup nestedServiceGroup = getNestedServiceGroup(serviceGroupName, serviceGroup); |
| |
| if (nestedServiceGroup == null) { |
| handleError("Service Group Definition not found for name " + serviceGroupName); |
| } |
| |
| if (log.isDebugEnabled()) { |
| log.debug("parsing application ... getScaleDependentForGroup: " + serviceGroupName); |
| } |
| |
| assert nestedServiceGroup != null; |
| if (nestedServiceGroup.getDependencies() != null) { |
| if (log.isDebugEnabled()) { |
| log.debug("parsing application ... getScaleDependentForGroup: dependencies != null "); |
| } |
| if (nestedServiceGroup.getDependencies().getScalingDependants() != null) { |
| |
| String[] scalingDependants = nestedServiceGroup.getDependencies().getScalingDependants(); |
| if (log.isDebugEnabled()) { |
| log.debug("parsing application ... getScaleDependentForGroup: scalingDependants != null # of: " + scalingDependants.length); |
| } |
| return scalingDependants; |
| } |
| } |
| |
| return null; |
| } |
| |
| |
| /** |
| * Get kill behaviour related to a Group |
| * |
| * @param serviceGroupName Group name |
| * @return String indicating the kill behavior |
| * @throws ApplicationDefinitionException if an error occurs |
| */ |
| private String getTerminationBehaviour(String serviceGroupName, ServiceGroup serviceGroup) throws ApplicationDefinitionException { |
| |
| ServiceGroup nestedServiceGroup = getNestedServiceGroup(serviceGroupName, serviceGroup); |
| |
| if (nestedServiceGroup == null) { |
| handleError("Service Group Definition not found for name " + serviceGroupName); |
| } |
| |
| assert nestedServiceGroup != null; |
| if (nestedServiceGroup.getDependencies() != null) { |
| return nestedServiceGroup.getDependencies().getTerminationBehaviour(); |
| } |
| |
| return null; |
| |
| } |
| |
| private ServiceGroup getNestedServiceGroup(String serviceGroupName, ServiceGroup serviceGroup) { |
| if (serviceGroup.getName().equals(serviceGroupName)) { |
| return serviceGroup; |
| } else if (serviceGroup.getGroups() != null) { |
| ServiceGroup[] groups = serviceGroup.getGroups(); |
| for (ServiceGroup sg : groups) { |
| return getNestedServiceGroup(serviceGroupName, sg); |
| } |
| } |
| return null; |
| |
| } |
| |
| /** |
| * Retrieves deployed service group |
| * |
| * @param serviceGroupName name of the Service Group |
| * @return ServiceGroup instance if exists |
| * @throws ApplicationDefinitionException if no Service Group found for the given serviceGroupName |
| */ |
| private ServiceGroup getServiceGroup(String serviceGroupName) throws ApplicationDefinitionException { |
| |
| try { |
| return RegistryManager.getInstance().getServiceGroup(serviceGroupName); |
| } catch (Exception e) { |
| String errorMsg = "Could not read cartridge group: " + serviceGroupName; |
| log.error(errorMsg, e); |
| throw new ApplicationDefinitionException(errorMsg, e); |
| } |
| } |
| |
| |
| /** |
| * Creates a ApplicationClusterContext object to keep information related to a Cluster in this Application |
| * |
| * @param appId Application id |
| * @param groupName Group name |
| * @param cartridge Cartridge information |
| * @param subscriptionKey Generated key for the Application |
| * @param tenantId Tenant Id of the tenant which deployed the Application |
| * @param repoUrl Repository URL |
| * @param alias alias specified for this Subscribable in the Application Definition |
| * @param clusterId Cluster id |
| * @param hostname Hostname |
| * @param deploymentPolicy Deployment policy used |
| * @param isLB if this cluster is an LB |
| * @param dependencyClustorIDs |
| * @return ApplicationClusterContext object with relevant information |
| * @throws ApplicationDefinitionException If any error occurs |
| */ |
| private ApplicationClusterContext createApplicationClusterContext(String appId, String groupName, Cartridge cartridge, |
| String subscriptionKey, int tenantId, String repoUrl, |
| String alias, String clusterId, String hostname, |
| String deploymentPolicy, boolean isLB, String tenantRange, |
| String[] dependencyAliases, Properties properties, String[] dependencyClustorIDs, |
| String[] exportMetadata, String[] importMetadata, String lvsVirtualIP) |
| throws ApplicationDefinitionException { |
| |
| // Create text payload |
| PayloadData payloadData = ApplicationUtils.createPayload(appId, groupName, cartridge, subscriptionKey, tenantId, clusterId, |
| hostname, repoUrl, alias, null, dependencyAliases, properties, oauthToken, dependencyClustorIDs, exportMetadata, importMetadata, lvsVirtualIP); |
| |
| String textPayload = payloadData.toString(); |
| if (log.isDebugEnabled()) { |
| log.debug("Payload :: " + textPayload); |
| } |
| return new ApplicationClusterContext(cartridge.getType(), clusterId, hostname, textPayload, deploymentPolicy, isLB, tenantRange, dependencyClustorIDs); |
| } |
| |
| public String createToken(String applicationId) throws AutoScalerException { |
| String token = null; |
| String ouathAppName = applicationId + Math.random(); |
| String serviceProviderName = ouathAppName; |
| |
| try { |
| OAuthAdminServiceClient.getServiceClient().registerOauthApplication(ouathAppName); |
| } catch (RemoteException e) { |
| throw new AutoScalerException(e); |
| } catch (OAuthAdminServiceException e) { |
| throw new AutoScalerException(e); |
| } |
| |
| String errorMessage = String.format("Could not create oauth token: [application-id] %s", applicationId); |
| |
| try { |
| token = IdentityApplicationManagementServiceClient.getServiceClient().createServiceProvider(ouathAppName, |
| serviceProviderName, applicationId); |
| } catch (RemoteException e) { |
| log.error(errorMessage, e); |
| throw new AutoScalerException(errorMessage, e); |
| } catch (OAuthAdminServiceException e) { |
| log.error(errorMessage, e); |
| throw new AutoScalerException(errorMessage, e); |
| } catch (OAuthProblemException e) { |
| log.error(errorMessage, e); |
| throw new AutoScalerException(errorMessage, e); |
| } catch (OAuthSystemException e) { |
| log.error(errorMessage, e); |
| throw new AutoScalerException(errorMessage, e); |
| } |
| |
| return token; |
| } |
| |
| private Cartridge getCartridge(String cartridgeType) throws ApplicationDefinitionException { |
| |
| try { |
| return CloudControllerServiceClient.getInstance().getCartridge(cartridgeType); |
| } catch (Exception e) { |
| log.error("Unable to get the cartridge: " + cartridgeType, e); |
| throw new ApplicationDefinitionException(e); |
| } |
| } |
| |
| private void handleError(String errorMsg) throws ApplicationDefinitionException { |
| log.error(errorMsg); |
| throw new ApplicationDefinitionException(errorMsg); |
| } |
| |
| public Map<String, Properties> getAliasToProperties() { |
| return aliasToProperties; |
| } |
| |
| public void setAliasToProperties(Map<String, Properties> aliasToProperties) { |
| this.aliasToProperties = aliasToProperties; |
| } |
| |
| public void addProperties(String alias, Properties properties) { |
| this.getAliasToProperties().put(alias, properties); |
| } |
| } |