@Override
  public Object doExecute() throws Exception {
    // check if the group exists
    Group group = groupManager.findGroupByName(groupName);
    if (group == null) {
      System.err.println("Cluster group " + groupName + " doesn't exist");
      return null;
    }

    // check if the producer is ON
    if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) {
      System.err.println("Cluster event producer is OFF");
      return null;
    }

    // check if the URL is allowed
    if (!isAllowed(group, Constants.URLS_CONFIG_CATEGORY, url, EventType.OUTBOUND)) {
      System.err.println("OBR URL " + url + " is blocked outbound for cluster group " + groupName);
      return null;
    }

    // update the OBR URLs in the cluster group
    Set<String> clusterUrls =
        clusterManager.getSet(
            Constants.URLS_DISTRIBUTED_SET_NAME + Configurations.SEPARATOR + groupName);
    clusterUrls.add(url);
    // update the OBR bundles in the cluster group
    Set<ObrBundleInfo> clusterBundles =
        clusterManager.getSet(
            Constants.BUNDLES_DISTRIBUTED_SET_NAME + Configurations.SEPARATOR + groupName);
    synchronized (obrService) {
      Repository repository = obrService.addRepository(url);
      Resource[] resources = repository.getResources();
      for (Resource resource : resources) {
        ObrBundleInfo info =
            new ObrBundleInfo(
                resource.getPresentationName(),
                resource.getSymbolicName(),
                resource.getVersion().toString());
        clusterBundles.add(info);
      }
      obrService.removeRepository(url);
    }

    // broadcast a cluster event
    ClusterObrUrlEvent event = new ClusterObrUrlEvent(url, Constants.URL_ADD_EVENT_TYPE);
    event.setSourceGroup(group);
    eventProducer.produce(event);

    return null;
  }
  @Override
  protected Object doExecute() throws Exception {
    // check if the group exists
    Group group = groupManager.findGroupByName(groupName);
    if (group == null) {
      System.err.println("Cluster group " + groupName + " doesn't exist");
      return null;
    }

    // check if the producer is ON
    if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) {
      System.err.println("Cluster event producer is OFF");
      return null;
    }

    // check if the config pid is allowed
    if (!isAllowed(group, Constants.CATEGORY, pid, EventType.OUTBOUND)) {
      System.err.println(
          "Configuration PID " + pid + " is blocked outbound for cluster group " + groupName);
      return null;
    }

    Map<String, Properties> clusterConfigurations =
        clusterManager.getMap(Constants.CONFIGURATION_MAP + Configurations.SEPARATOR + groupName);
    if (clusterConfigurations != null) {
      // update the configurations in the cluster group
      Properties properties = clusterConfigurations.get(pid);
      if (properties == null) {
        properties = new Properties();
      }
      Object currentValue = properties.get(key);
      if (currentValue == null) {
        properties.put(key, value);
      } else if (currentValue instanceof String) {
        properties.put(key, currentValue + value);
      } else {
        System.err.println("Append failed: current value is not a String");
        return null;
      }
      clusterConfigurations.put(pid, properties);

      // broadcast the cluster event
      ClusterConfigurationEvent event = new ClusterConfigurationEvent(pid);
      event.setSourceGroup(group);
      eventProducer.produce(event);
    } else {
      System.out.println("No configuration found in cluster group " + groupName);
    }
    return null;
  }
  @Override
  protected Object doExecute() throws Exception {
    // check if the group exists
    Group group = groupManager.findGroupByName(groupName);
    if (group == null) {
      System.err.println("Cluster group " + groupName + " doesn't exist");
      return null;
    }

    // check if the producer is ON
    if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) {
      System.err.println("Cluster event producer is OFF for this node");
      return null;
    }

    ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());

    try {

      // get cluster features
      Map<String, FeatureState> clusterFeatures =
          clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);

      CellarSupport support = new CellarSupport();
      support.setGroupManager(groupManager);
      support.setClusterManager(clusterManager);
      support.setConfigurationAdmin(configurationAdmin);

      for (String feature : features) {
        String[] split = feature.split("/");
        String name = split[0];
        String version = null;
        if (split.length == 2) {
          version = split[1];
        }

        FeatureState found = null;
        String foundKey = null;
        for (String k : clusterFeatures.keySet()) {
          FeatureState f = clusterFeatures.get(k);
          foundKey = k;
          if (version == null) {
            if (f.getName().equals(name)) {
              found = f;
              break;
            }
          } else {
            if (f.getName().equals(name) && f.getVersion().equals(version)) {
              found = f;
              break;
            }
          }
        }
        if (found == null) {
          if (version == null)
            throw new IllegalArgumentException(
                "Feature " + name + " doesn't exist in cluster group " + groupName);
          else
            throw new IllegalArgumentException(
                "Feature " + name + "/" + version + " doesn't exist in cluster group " + groupName);
        }

        // check if the feature is allowed (outbound)
        if (!support.isAllowed(group, Constants.CATEGORY, found.getName(), EventType.OUTBOUND)) {
          System.err.println(
              "Feature " + found.getName() + " is blocked outbound for cluster group " + groupName);
          continue;
        }

        // update the cluster state
        found.setInstalled(false);
        clusterFeatures.put(foundKey, found);

        // broadcast the cluster event
        ClusterFeaturesEvent event =
            new ClusterFeaturesEvent(
                found.getName(),
                found.getVersion(),
                false,
                noRefresh,
                false,
                FeatureEvent.EventType.FeatureUninstalled);
        event.setSourceGroup(group);
        eventProducer.produce(event);
      }
    } finally {
      Thread.currentThread().setContextClassLoader(originalClassLoader);
    }

    return null;
  }