// Path: chassis-bootstrap/src/main/java/com/kixeye/chassis/bootstrap/BootstrapException.java
// @SuppressWarnings("serial")
// public class BootstrapException extends RuntimeException {
//     public BootstrapException(String message) {
//         super(message);
//     }
// 
//     public BootstrapException(String message, Exception e) {
//         super(message, e);
//     }
// 
//     public static void resourceLoadingFailed(String path, String originalPath, Exception e) {
//         throw new ResourceLoadingException(path, originalPath, e);
//     }
// 
//     public static void resourceLoadingFailed(String path, Exception e) {
//         throw new ResourceLoadingException(path, e);
//     }
// 
//     public static void missingApplicationVersion() {
//         throw new MissingApplicationVersionException();
//     }
// 
//     public static void zookeeperInitializationFailed(String zookeeperHost, String configPath, Exception e) {
//         throw new ZookeeperInitializationException(zookeeperHost, configPath, e);
//     }
// 
//     public static void zookeeperInitializationFailed(String zookeeperHost, Exhibitors exhibitors, Exception e) {
//         throw new ZookeeperInitializationException(zookeeperHost, exhibitors, e);
//     }
// 
//     public static void configurationNotFound(String message, Exception e) {
//         throw new ApplicationConfigurationNotFoundException(message, e);
//     }
// 
//     public static void moduleKeysConflictFound(String propertyFile, String[] propertyFiles) {
//         throw new ConflictingModuleConfigurationKeysException(propertyFile, propertyFiles);
//     }
// 
//     public static void zookeeperExhibitorConflict() {
//         throw new ZookeeperExhibitorUsageException();
//     }
// 
//     public static void applicationRestartAttempted(){
//         throw new ApplicationRestartException();
//     }
// 
//     public static class MissingApplicationVersionException extends BootstrapException {
// 
//         public MissingApplicationVersionException() {
//             super("application version not found.  Version should be defined in application jar's MANIFEST.MF (Implementation-Version), as system property (app.version) or application's configuration (app.version).");
//         }
//     }
// 
//     public static class ZookeeperExhibitorUsageException extends BootstrapException{
// 
//         public ZookeeperExhibitorUsageException() {
//             super("Exhibitor and Zookeeper cannot be configured together. One or the other must be used.");
//         }
//     }
// 
//     public static class ResourceLoadingException extends BootstrapException {
// 
//         public ResourceLoadingException(String path, String originalPath, Exception e) {
//             super("Unable to load application configuration resource " + path + ". Original path: " + originalPath, e);
//         }
// 
//         public ResourceLoadingException(String path, Exception e) {
//             super("Unable to load application configuration resource " + path + ".", e);
//         }
//     }
// 
//     public static class ZookeeperInitializationException extends BootstrapException {
//         public ZookeeperInitializationException(String zookeeperHost, String configPath, Exception e) {
//             super("Unable to initialize zookeeper configuration for zookeeper " + zookeeperHost + " at path " + configPath + ".", e);
//         }
// 
//         public ZookeeperInitializationException(String zookeeperHost, Exhibitors exhibitors, Exception e) {
//             super("Unable to initialize zookeeper configuration for zookeeper " + zookeeperHost + ". Exhibitors:" + ReflectionToStringBuilder.reflectionToString(exhibitors), e);
//         }
//     }
// 
//     public static class ApplicationConfigurationNotFoundException extends BootstrapException {
//         public ApplicationConfigurationNotFoundException(String message, Exception e) {
//             super(message, e);
//         }
//     }
// 
//     public static class ConflictingModuleConfigurationKeysException extends BootstrapException {
//         public ConflictingModuleConfigurationKeysException(String propertyFile, String[] propertyFiles) {
//             super("Duplicate key found in default property file "
//                     + propertyFile
//                     + ". Check that all default property files contains unique keys. Files: "
//                     + Arrays.toString(propertyFiles));
//         }
//     }
// 
//     public static class ApplicationRestartException extends BootstrapException{
//         public ApplicationRestartException(){
//             super("Application cannot be restarted.");
//         }
//     }
// 
// }


import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.google.common.base.Preconditions;
import com.kixeye.chassis.bootstrap.BootstrapException;
import com.kixeye.chassis.bootstrap.configuration.ConfigurationWriter;
import com.kixeye.chassis.bootstrap.configuration.Configurations;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.KeeperException.NodeExistsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
        Preconditions.checkArgument(StringUtils.isNotBlank(version), "version is required");

        this.configPath = String.format("/%s/%s/%s/config", environment, applicationName, version);
        this.curatorFramework = curatorFramework;
        this.allowOverwrite = allowOverwrite;
    }

    @Override
    public void write(Configuration configuration, Filter propertyFilter) {
        Map<String, ?> config = Configurations.asMap(configuration);
        boolean exists = validateConfigPathAbsent();
        if (!exists) {
            writeConfigPath(configPath);
        }
        ArrayList<Set<String>> deltas = findDeltas(config);
        for (String key : deltas.get(0)) {
            String value = config.get(key) + "";
            String path = configPath + "/" + key;
            writeKey(path, value, propertyFilter);
        }
        for (String key : deltas.get(1)) {
            deleteKey(configPath + "/" + key);
        }
    }

    private void deleteKey(String key) {
        try {
            LOGGER.info("deleting key {}...", key);
            curatorFramework.delete().forPath(key);
        } catch (Exception e) {
// Path: chassis-bootstrap/src/main/java/com/kixeye/chassis/bootstrap/BootstrapException.java
// @SuppressWarnings("serial")
// public class BootstrapException extends RuntimeException {
//     public BootstrapException(String message) {
//         super(message);
//     }
// 
//     public BootstrapException(String message, Exception e) {
//         super(message, e);
//     }
// 
//     public static void resourceLoadingFailed(String path, String originalPath, Exception e) {
//         throw new ResourceLoadingException(path, originalPath, e);
//     }
// 
//     public static void resourceLoadingFailed(String path, Exception e) {
//         throw new ResourceLoadingException(path, e);
//     }
// 
//     public static void missingApplicationVersion() {
//         throw new MissingApplicationVersionException();
//     }
// 
//     public static void zookeeperInitializationFailed(String zookeeperHost, String configPath, Exception e) {
//         throw new ZookeeperInitializationException(zookeeperHost, configPath, e);
//     }
// 
//     public static void zookeeperInitializationFailed(String zookeeperHost, Exhibitors exhibitors, Exception e) {
//         throw new ZookeeperInitializationException(zookeeperHost, exhibitors, e);
//     }
// 
//     public static void configurationNotFound(String message, Exception e) {
//         throw new ApplicationConfigurationNotFoundException(message, e);
//     }
// 
//     public static void moduleKeysConflictFound(String propertyFile, String[] propertyFiles) {
//         throw new ConflictingModuleConfigurationKeysException(propertyFile, propertyFiles);
//     }
// 
//     public static void zookeeperExhibitorConflict() {
//         throw new ZookeeperExhibitorUsageException();
//     }
// 
//     public static void applicationRestartAttempted(){
//         throw new ApplicationRestartException();
//     }
// 
//     public static class MissingApplicationVersionException extends BootstrapException {
// 
//         public MissingApplicationVersionException() {
//             super("application version not found.  Version should be defined in application jar's MANIFEST.MF (Implementation-Version), as system property (app.version) or application's configuration (app.version).");
//         }
//     }
// 
//     public static class ZookeeperExhibitorUsageException extends BootstrapException{
// 
//         public ZookeeperExhibitorUsageException() {
//             super("Exhibitor and Zookeeper cannot be configured together. One or the other must be used.");
//         }
//     }
// 
//     public static class ResourceLoadingException extends BootstrapException {
// 
//         public ResourceLoadingException(String path, String originalPath, Exception e) {
//             super("Unable to load application configuration resource " + path + ". Original path: " + originalPath, e);
//         }
// 
//         public ResourceLoadingException(String path, Exception e) {
//             super("Unable to load application configuration resource " + path + ".", e);
//         }
//     }
// 
//     public static class ZookeeperInitializationException extends BootstrapException {
//         public ZookeeperInitializationException(String zookeeperHost, String configPath, Exception e) {
//             super("Unable to initialize zookeeper configuration for zookeeper " + zookeeperHost + " at path " + configPath + ".", e);
//         }
// 
//         public ZookeeperInitializationException(String zookeeperHost, Exhibitors exhibitors, Exception e) {
//             super("Unable to initialize zookeeper configuration for zookeeper " + zookeeperHost + ". Exhibitors:" + ReflectionToStringBuilder.reflectionToString(exhibitors), e);
//         }
//     }
// 
//     public static class ApplicationConfigurationNotFoundException extends BootstrapException {
//         public ApplicationConfigurationNotFoundException(String message, Exception e) {
//             super(message, e);
//         }
//     }
// 
//     public static class ConflictingModuleConfigurationKeysException extends BootstrapException {
//         public ConflictingModuleConfigurationKeysException(String propertyFile, String[] propertyFiles) {
//             super("Duplicate key found in default property file "
//                     + propertyFile
//                     + ". Check that all default property files contains unique keys. Files: "
//                     + Arrays.toString(propertyFiles));
//         }
//     }
// 
//     public static class ApplicationRestartException extends BootstrapException{
//         public ApplicationRestartException(){
//             super("Application cannot be restarted.");
//         }
//     }
// 
// }


// Path: chassis-bootstrap/src/main/java/com/kixeye/chassis/bootstrap/configuration/zookeeper/ZookeeperConfigurationWriter.java
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.google.common.base.Preconditions;
import com.kixeye.chassis.bootstrap.BootstrapException;
import com.kixeye.chassis.bootstrap.configuration.ConfigurationWriter;
import com.kixeye.chassis.bootstrap.configuration.Configurations;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.KeeperException.NodeExistsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

        Preconditions.checkArgument(StringUtils.isNotBlank(version), "version is required");

        this.configPath = String.format("/%s/%s/%s/config", environment, applicationName, version);
        this.curatorFramework = curatorFramework;
        this.allowOverwrite = allowOverwrite;
    }

    @Override
    public void write(Configuration configuration, Filter propertyFilter) {
        Map<String, ?> config = Configurations.asMap(configuration);
        boolean exists = validateConfigPathAbsent();
        if (!exists) {
            writeConfigPath(configPath);
        }
        ArrayList<Set<String>> deltas = findDeltas(config);
        for (String key : deltas.get(0)) {
            String value = config.get(key) + "";
            String path = configPath + "/" + key;
            writeKey(path, value, propertyFilter);
        }
        for (String key : deltas.get(1)) {
            deleteKey(configPath + "/" + key);
        }
    }

    private void deleteKey(String key) {
        try {
            LOGGER.info("deleting key {}...", key);
            curatorFramework.delete().forPath(key);
        } catch (Exception e) {
            throw new BootstrapException("Failed to delete key " + key, e);