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

[1/2] git commit: fixing service level lb subscription issues for MT scenarios

Updated Branches:
  refs/heads/master c59170bf3 -> fbe0ebcc4


fixing service level lb subscription issues for MT scenarios


Project: http://git-wip-us.apache.org/repos/asf/incubator-stratos/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-stratos/commit/f060ee6f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-stratos/tree/f060ee6f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-stratos/diff/f060ee6f

Branch: refs/heads/master
Commit: f060ee6fe16eff6fadc5abbdcdfc27f7c9074f15
Parents: db67a55
Author: Isuru <is...@wso2.com>
Authored: Thu Jan 23 18:09:03 2014 +0530
Committer: Isuru <is...@wso2.com>
Committed: Thu Jan 23 18:09:03 2014 +0530

----------------------------------------------------------------------
 .../stratos/manager/deploy/service/Service.java |   3 +-
 .../service/ServiceDeploymentManager.java       | 226 ++++++++-----------
 .../service/multitenant/MultiTenantService.java |   7 +-
 .../multitenant/lb/MultiTenantLBService.java    |   7 +-
 .../tenancy/SubscriptionTenancyBehaviour.java   |   2 +-
 .../rest/endpoint/services/ServiceUtils.java    |   2 +-
 6 files changed, 111 insertions(+), 136 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-stratos/blob/f060ee6f/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/Service.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/Service.java b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/Service.java
index 6aa540c..2db19ea 100644
--- a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/Service.java
+++ b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/Service.java
@@ -22,6 +22,7 @@ package org.apache.stratos.manager.deploy.service;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.stratos.cloud.controller.pojo.CartridgeInfo;
+import org.apache.stratos.cloud.controller.pojo.Properties;
 import org.apache.stratos.cloud.controller.pojo.Property;
 import org.apache.stratos.manager.client.CloudControllerServiceClient;
 import org.apache.stratos.manager.exception.ADCException;
@@ -61,7 +62,7 @@ public abstract class Service implements Serializable {
         this.subscriptionKey = CartridgeSubscriptionUtils.generateSubscriptionKey();
     }
 
-    public void deploy () throws ADCException, UnregisteredCartridgeException {
+    public void deploy (Properties properties) throws ADCException, UnregisteredCartridgeException {
 
         //generate the cluster ID (domain)for the service
         setClusterId(type + "." + cartridgeInfo.getHostName() + ".domain");

http://git-wip-us.apache.org/repos/asf/incubator-stratos/blob/f060ee6f/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/ServiceDeploymentManager.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/ServiceDeploymentManager.java b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/ServiceDeploymentManager.java
index 712aa5b..534e4c3 100644
--- a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/ServiceDeploymentManager.java
+++ b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/ServiceDeploymentManager.java
@@ -100,6 +100,7 @@ public class ServiceDeploymentManager {
         LoadbalancerConfig lbConfig = cartridgeInfo.getLbConfig();
 
         if (lbConfig == null || lbConfig.getProperties() == null) {
+
             if (log.isDebugEnabled()) {
                 log.debug("This Service does not require a load balancer. " + "[Service Name] " +
                           type);
@@ -150,7 +151,7 @@ public class ServiceDeploymentManager {
                     String[] clusterIds = clusterIdsVal.split(",");
 
                     for (String clusterId : clusterIds) {
-                        
+
                             try {
                             	AutoscalerServiceClient.getServiceClient().checkLBExistenceAgainstPolicy(clusterId,
                             			deploymentPolicyName);
@@ -158,7 +159,7 @@ public class ServiceDeploymentManager {
                                 // we don't need to throw the error here.
                                 log.error(ex.getMessage(), ex);
                             }
-                        
+
                     }
 
                     property.setValue(name);
@@ -172,7 +173,7 @@ public class ServiceDeploymentManager {
                             log.debug("This cartridge uses default load balancer. " + "[Type] " +
                                       type);
                         }
-                        
+
                             try {
                                 // get the valid policies for lb cartridge
                                 DeploymentPolicy[] lbCartridgeDepPolicies =
@@ -185,20 +186,26 @@ public class ServiceDeploymentManager {
                                         if (!AutoscalerServiceClient.getServiceClient().checkDefaultLBExistenceAgainstPolicy(deploymentPolicyName)) {
 
                                             // if lb cluster doesn't exist
-                                            lbCartridgeInfo.addProperties(property);
-                                            /*subscribeToLb(lbCartridgeType,
-                                                          lbAlias,
-                                                          lbCartridgeInfo.getDefaultAutoscalingPolicy(),
-                                                          deploymentPolicyName, tenantId,
-                                                          userName,
-                                                          tenantDomain,
-                                                          lbCartridgeInfo.getProperties());*/
                                             lbService = new MultiTenantLBService(lbCartridgeType,
                                                     lbCartridgeInfo.getDefaultAutoscalingPolicy(),
                                                     deploymentPolicyName, tenantId,
                                                     lbCartridgeInfo,
                                                     tenantRange);
-                                            lbService.deploy();
+
+                                            Properties lbDeploymentProperties = new Properties();
+
+                                            // check if there are properties in LB cartridge info
+                                            Property [] cartridgeInfoProps = lbCartridgeInfo.getProperties();
+                                            if (cartridgeInfoProps != null && cartridgeInfoProps.length > 0) {
+                                                lbDeploymentProperties.setProperties(combine(lbCartridgeInfo.getProperties(), new Property[]{property}));
+                                            } else {
+                                                lbDeploymentProperties.setProperties(new Property[]{property});
+                                            }
+
+                                            lbService.deploy(lbDeploymentProperties);
+
+                                            // persist
+                                            persist(lbService);
                                         }
                                     }
                                 }
@@ -207,110 +214,94 @@ public class ServiceDeploymentManager {
                                 // we don't need to throw the error here.
                                 log.error(ex.getMessage(), ex);
                             }
-                        
+
 
                         lbRefProp.add(property);
                         break;
-                    } else if (Constants.SERVICE_AWARE_LOAD_BALANCER.equals(name)) {
-                        if ("true".equals(value)) {
-                            property.setValue(name);
-                            if (log.isDebugEnabled()) {
-                                log.debug("This cartridge uses a service aware load balancer. " +
-                                          "[Type] " + type);
-                            }
-                            
-                                try {
-
-                                    // get the valid policies for lb cartridge
-                                    DeploymentPolicy[] lbCartridgeDepPolicies =
-                                    	AutoscalerServiceClient.getServiceClient().getDeploymentPolicies(lbCartridgeType);
-                                    // traverse deployment policies of lb cartridge
-                                    for (DeploymentPolicy policy : lbCartridgeDepPolicies) {
-                                        // check existence of the subscribed policy
-                                        if (deploymentPolicyName.equals(policy.getId())) {
-
-                                            if (!AutoscalerServiceClient.getServiceClient().checkServiceLBExistenceAgainstPolicy(type,
-                                                                                                              deploymentPolicyName)) {
-
-                                                lbCartridgeInfo.addProperties(property);
-                                                /*subscribeToLb(lbCartridgeType,
-                                                              lbAlias,
-                                                              lbCartridgeInfo.getDefaultAutoscalingPolicy(),
-                                                              deploymentPolicyName,
-                                                              tenantId, 
-                                                              userName,
-                                                              tenantDomain,
-                                                              lbCartridgeInfo.getProperties());*/
-                                                lbService = new MultiTenantLBService(lbCartridgeType,
-                                                        lbCartridgeInfo.getDefaultAutoscalingPolicy(),
-                                                        deploymentPolicyName, tenantId,
-                                                        lbCartridgeInfo,
-                                                        tenantRange);
-                                                lbService.deploy();
-                                            }
+                    }
+                } else if (Constants.SERVICE_AWARE_LOAD_BALANCER.equals(name)) {
+                    if ("true".equals(value)) {
+                        property.setValue(name);
+                        if (log.isDebugEnabled()) {
+                            log.debug("This cartridge uses a service aware load balancer. " +
+                                    "[Type] " + type);
+                        }
+
+                        // add a property for the service type
+                        Property loadBalancedServiceTypeProperty = new Property();
+                        loadBalancedServiceTypeProperty.setName(Constants.LOAD_BALANCED_SERVICE_TYPE);
+                        loadBalancedServiceTypeProperty.setValue(type);
+
+                        try {
+
+                            // get the valid policies for lb cartridge
+                            DeploymentPolicy[] lbCartridgeDepPolicies =
+                                    AutoscalerServiceClient.getServiceClient().getDeploymentPolicies(lbCartridgeType);
+                            // traverse deployment policies of lb cartridge
+                            for (DeploymentPolicy policy : lbCartridgeDepPolicies) {
+
+                                // check existence of the subscribed policy
+                                if (deploymentPolicyName.equals(policy.getId())) {
+
+                                    if (!AutoscalerServiceClient.getServiceClient().checkServiceLBExistenceAgainstPolicy(type,
+                                            deploymentPolicyName)) {
+
+                                        lbCartridgeInfo.addProperties(property);
+                                        lbCartridgeInfo.addProperties(loadBalancedServiceTypeProperty);
+
+                                        lbService = new MultiTenantLBService(lbCartridgeType,
+                                                lbCartridgeInfo.getDefaultAutoscalingPolicy(),
+                                                deploymentPolicyName, tenantId,
+                                                lbCartridgeInfo,
+                                                tenantRange);
+
+                                        Properties lbDeploymentProperties = new Properties();
+
+                                        // check if there are properties in LB cartridge info
+                                        Property [] cartridgeInfoProps = lbCartridgeInfo.getProperties();
+                                        if (cartridgeInfoProps != null && cartridgeInfoProps.length > 0) {
+                                            lbDeploymentProperties.setProperties(combine(lbCartridgeInfo.getProperties(), new Property[]{property, loadBalancedServiceTypeProperty}));
+                                        } else {
+                                            lbDeploymentProperties.setProperties(new Property[]{property, loadBalancedServiceTypeProperty});
                                         }
-                                    }
 
-                                } catch (Exception ex) {
-                                    // we don't need to throw the error here.
-                                    log.error(ex.getMessage(), ex);
+                                        lbService.deploy(lbDeploymentProperties);
+
+                                        // persist
+                                        persist(lbService);
+                                    }
                                 }
-                           
+                            }
 
-                            lbRefProp.add(property);
-                            break;
+                        } catch (Exception ex) {
+                            // we don't need to throw the error here.
+                            log.error(ex.getMessage(), ex);
                         }
+
+
+                        lbRefProp.add(property);
+                        break;
                     }
                 }
             }
         }
 
-
-
-        
         Service service = new MultiTenantService(type, autoscalingPolicyName, deploymentPolicyName, tenantId, cartridgeInfo, tenantRange);
 
-        /*//generate the cluster ID (domain)for the service
-        service.setClusterId(type + "." + cartridgeInfo.getHostName() + ".domain");
-        //host name is the hostname defined in cartridge definition
-        service.setHostName(cartridgeInfo.getHostName());
-
-        //Create payload
-        BasicPayloadData basicPayloadData = CartridgeSubscriptionUtils.createBasicPayload(service);
-        //populate
-        basicPayloadData.populatePayload();
-        PayloadData payloadData = PayloadFactory.getPayloadDataInstance(cartridgeInfo.getProvider(),
-                cartridgeInfo.getType(), basicPayloadData);
-
-        // get the payload parameters defined in the cartridge definition file for this cartridge type
-        if (cartridgeInfo.getProperties() != null && cartridgeInfo.getProperties().length != 0) {
-
-            for (Property property : cartridgeInfo.getProperties()) {
-                // check if a property is related to the payload. Currently this is done by checking if the
-                // property name starts with 'payload_parameter.' suffix. If so the payload param name will
-                // be taken as the substring from the index of '.' to the end of the property name.
-                if (property.getName()
-                        .startsWith(CartridgeConstants.CUSTOM_PAYLOAD_PARAM_NAME_PREFIX)) {
-                    String payloadParamName = property.getName();
-                    payloadData.add(payloadParamName.substring(payloadParamName.indexOf(".") + 1), property.getValue());
-                }
-            }
-        }
+        //deploy the service
+        Properties serviceDeploymentProperties = new Properties();
+        serviceDeploymentProperties.setProperties(lbRefProp.toArray(new Property[0]));
+        service.deploy(serviceDeploymentProperties);
 
-        //set PayloadData instance
-        service.setPayloadData(payloadData);*/
+        // persist
+        persist(service);
 
-        //deploy the service
-        service.deploy();
+        return service;
+    }
 
-        //persist Service
-        /*try {
-			PersistenceManager.persistService(service);
-		} catch (Exception e) {
-            String message = "Error getting info for " + type;
-            log.error(message, e);
-            throw new ADCException(message, e);
-        }*/
+    private void persist (Service service) throws ADCException {
+
+        DataInsertionAndRetrievalManager dataInsertionAndRetrievalManager = new DataInsertionAndRetrievalManager();
 
         try {
             dataInsertionAndRetrievalManager.persistService(service);
@@ -320,8 +311,6 @@ public class ServiceDeploymentManager {
             log.error(errorMsg, e);
             throw new ADCException(errorMsg, e);
         }
-
-        return service;
     }
 
     public void undeployService (String type) throws ADCException {
@@ -371,31 +360,14 @@ public class ServiceDeploymentManager {
             throw new ADCException(errorMsg, e);
         }
     }
-    
-    /*private void subscribeToLb(String cartridgeType, String lbAlias,
-            String defaultAutoscalingPolicy, String deploymentPolicy,
-            int tenantId, String userName, String tenantDomain, Property[] props) throws ADCException {
-            
-            try {
-                if(log.isDebugEnabled()) {
-                    log.debug("Subscribing to a load balancer [cartridge] "+cartridgeType+" [alias] "+lbAlias);
-                }
-                CartridgeSubscription cartridgeSubscription = 
-                        cartridgeSubsciptionManager.subscribeToCartridgeWithProperties(cartridgeType, lbAlias.trim(), defaultAutoscalingPolicy, 
-                                                                         deploymentPolicy,
-                                                                         tenantDomain, 
-                                                                         tenantId,
-                                                                         userName, "git", null, false, null, null, props);
-                
-                cartridgeSubsciptionManager.registerCartridgeSubscription(cartridgeSubscription);
-                
-                if(log.isDebugEnabled()) {
-                    log.debug("Successfully subscribed to a load balancer [cartridge] "+cartridgeType+" [alias] "+lbAlias);
-                }
-            } catch (Exception e) {
-                String msg = "Error while subscribing to load balancer cartridge [type] "+cartridgeType;
-                log.error(msg, e);
-                throw new ADCException(msg, e);
-            }
-        }*/
+
+    private static Property[] combine (Property[] propertyArray1, Property[] propertyArray2) {
+
+        int length = propertyArray1.length + propertyArray2.length;
+        Property[] combinedProperties = new Property[length];
+        System.arraycopy(propertyArray1, 0, combinedProperties, 0, propertyArray1.length);
+        System.arraycopy(propertyArray2, 0, combinedProperties, propertyArray1.length, propertyArray2.length);
+
+        return combinedProperties;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-stratos/blob/f060ee6f/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/MultiTenantService.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/MultiTenantService.java b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/MultiTenantService.java
index 8dee8b6..2b4fce3 100644
--- a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/MultiTenantService.java
+++ b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/MultiTenantService.java
@@ -21,6 +21,7 @@ package org.apache.stratos.manager.deploy.service.multitenant;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.stratos.cloud.controller.pojo.Properties;
 import org.apache.stratos.manager.deploy.service.Service;
 import org.apache.stratos.manager.exception.ADCException;
 import org.apache.stratos.manager.exception.UnregisteredCartridgeException;
@@ -38,13 +39,13 @@ public class MultiTenantService extends Service {
     }
 
     @Override
-    public void deploy() throws ADCException, UnregisteredCartridgeException {
+    public void deploy(Properties properties) throws ADCException, UnregisteredCartridgeException {
 
-        super.deploy();
+        super.deploy(properties);
 
         //register the service
         ApplicationManagementUtil.registerService(getType(), getClusterId(), CartridgeConstants.DEFAULT_SUBDOMAIN,
                 getPayloadData().getCompletePayloadData(), getTenantRange(), getHostName(), getAutoscalingPolicyName(),
-                getDeploymentPolicyName(), null);
+                getDeploymentPolicyName(), properties);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-stratos/blob/f060ee6f/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/lb/MultiTenantLBService.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/lb/MultiTenantLBService.java b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/lb/MultiTenantLBService.java
index 07bb6f1..55f9592 100644
--- a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/lb/MultiTenantLBService.java
+++ b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/deploy/service/multitenant/lb/MultiTenantLBService.java
@@ -22,6 +22,7 @@ package org.apache.stratos.manager.deploy.service.multitenant.lb;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.stratos.cloud.controller.pojo.CartridgeInfo;
+import org.apache.stratos.cloud.controller.pojo.Properties;
 import org.apache.stratos.manager.deploy.service.Service;
 import org.apache.stratos.manager.exception.ADCException;
 import org.apache.stratos.manager.exception.UnregisteredCartridgeException;
@@ -37,13 +38,13 @@ public class MultiTenantLBService extends Service {
     }
 
     @Override
-    public void deploy() throws ADCException, UnregisteredCartridgeException {
+    public void deploy(Properties properties) throws ADCException, UnregisteredCartridgeException {
 
-        super.deploy();
+        super.deploy(properties);
 
         //register the service
         ApplicationManagementUtil.registerService(getType(), getClusterId(), CartridgeConstants.DEFAULT_SUBDOMAIN,
                 getPayloadData().getCompletePayloadData(), getTenantRange(), getHostName(), getAutoscalingPolicyName(),
-                getDeploymentPolicyName(), null);
+                getDeploymentPolicyName(), properties);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-stratos/blob/f060ee6f/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/subscription/tenancy/SubscriptionTenancyBehaviour.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/subscription/tenancy/SubscriptionTenancyBehaviour.java b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/subscription/tenancy/SubscriptionTenancyBehaviour.java
index 774dd22..7bb4b0a 100644
--- a/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/subscription/tenancy/SubscriptionTenancyBehaviour.java
+++ b/components/org.apache.stratos.manager/src/main/java/org/apache/stratos/manager/subscription/tenancy/SubscriptionTenancyBehaviour.java
@@ -30,7 +30,7 @@ import java.io.Serializable;
 
 public abstract class SubscriptionTenancyBehaviour implements Serializable {
 
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 6529685098267757690L;
 
     public abstract void createSubscription(CartridgeSubscription cartridgeSubscription)
             throws ADCException, AlreadySubscribedException;

http://git-wip-us.apache.org/repos/asf/incubator-stratos/blob/f060ee6f/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/services/ServiceUtils.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/services/ServiceUtils.java b/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/services/ServiceUtils.java
index ca1e164..e439e88 100644
--- a/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/services/ServiceUtils.java
+++ b/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/services/ServiceUtils.java
@@ -52,7 +52,7 @@ import java.util.*;
 import java.util.regex.Pattern;
 
 public class ServiceUtils {
-    private static Log log = LogFactory.getLog(StratosAdmin.class);
+    private static Log log = LogFactory.getLog(ServiceUtils.class);
     private static CartridgeSubscriptionManager cartridgeSubsciptionManager = new CartridgeSubscriptionManager();
     private static ServiceDeploymentManager serviceDeploymentManager = new ServiceDeploymentManager();
 


[2/2] git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-stratos into service_deployment

Posted by is...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-stratos into service_deployment


Project: http://git-wip-us.apache.org/repos/asf/incubator-stratos/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-stratos/commit/fbe0ebcc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-stratos/tree/fbe0ebcc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-stratos/diff/fbe0ebcc

Branch: refs/heads/master
Commit: fbe0ebcc48543256f097dc4e83ac00c40d3a332f
Parents: f060ee6 c59170b
Author: Isuru <is...@wso2.com>
Authored: Thu Jan 23 18:10:02 2014 +0530
Committer: Isuru <is...@wso2.com>
Committed: Thu Jan 23 18:10:02 2014 +0530

----------------------------------------------------------------------
 .../controller/hive/HiveQueryExecutor.java      |  36 +-
 .../impl/CloudControllerServiceImpl.java        |  24 +-
 .../controller/pojo/CartridgeInstanceData.java  |  10 -
 .../CartridgeInstanceDataPublisher.java         | 211 +++++++++
 .../CartridgeInstanceDataPublisherTask.java     | 459 -------------------
 .../runtime/FasterLookUpDataHolder.java         |  20 +-
 .../controller/topology/TopologyBuilder.java    |  25 +
 .../util/CloudControllerConstants.java          |  14 +-
 8 files changed, 295 insertions(+), 504 deletions(-)
----------------------------------------------------------------------