text:
namespace namedproptest
{
	TEST_CLASS(namedproptest)
	{
	public:
		// Without this, clang gets weird
		static const bool dummy_var = true;

		const std::vector<BYTE> sig1 = {1, 2, 3, 4};
		const std::vector<BYTE> sig2 = {5, 6, 7, 8, 9};

		const MAPINAMEID formStorageID = {const_cast<LPGUID>(&guid::PSETID_Common), MNID_ID, dispidFormStorage};
		const MAPINAMEID formStorageIDLog = {const_cast<LPGUID>(&guid::PSETID_Log), MNID_ID, dispidFormStorage};
		const MAPINAMEID formStorageName = {
			const_cast<LPGUID>(&guid::PSETID_Common), MNID_STRING, {.lpwstrName = const_cast<LPWSTR>(L"name")}};
		const MAPINAMEID formStorageName2 = {
			const_cast<LPGUID>(&guid::PSETID_Common), MNID_STRING, {.lpwstrName = const_cast<LPWSTR>(L"name2")}};
		const MAPINAMEID pageDirStreamID = {const_cast<LPGUID>(&guid::PSETID_Common), MNID_ID, dispidPageDirStream};
		const MAPINAMEID propDefStream = {const_cast<LPGUID>(&guid::PSETID_Common), MNID_ID, dispidPropDefStream};

		const std::shared_ptr<cache::namedPropCacheEntry> formStorage1 =
			cache::namedPropCacheEntry::make(&formStorageID, 0x1111, sig1);
		const std::shared_ptr<cache::namedPropCacheEntry> formStorage2 =
			cache::namedPropCacheEntry::make(&formStorageID, 0x1111, sig2);
		const std::shared_ptr<cache::namedPropCacheEntry> formStorageLog =
			cache::namedPropCacheEntry::make(&formStorageIDLog, 0x1111, sig1);

		const std::shared_ptr<cache::namedPropCacheEntry> formStorageProp =
			cache::namedPropCacheEntry::make(&formStorageName, 0x1111, sig1);
		const std::shared_ptr<cache::namedPropCacheEntry> formStorageProp1 =
			cache::namedPropCacheEntry::make(&formStorageName2, 0x1111, sig1);

		const std::shared_ptr<cache::namedPropCacheEntry> prop1 =
			cache::namedPropCacheEntry::make(&formStorageID, 0x1111);
		const std::shared_ptr<cache::namedPropCacheEntry> prop2 =
			cache::namedPropCacheEntry::make(&formStorageName, 0x2222);
		const std::shared_ptr<cache::namedPropCacheEntry> prop3 =
			cache::namedPropCacheEntry::make(&pageDirStreamID, 0x3333);
		const std::vector<std::shared_ptr<cache::namedPropCacheEntry>> ids1 = {prop1, prop2};
		const std::vector<std::shared_ptr<cache::namedPropCacheEntry>> ids2 = {prop3};

		TEST_CLASS_INITIALIZE(initialize)
		{
			unittest::init();
			if ((registry::debugTag & static_cast<DWORD>(output::dbgLevel::All)) ==
				static_cast<DWORD>(output::dbgLevel::All))
			{
				registry::debugTag |= static_cast<DWORD>(output::dbgLevel::NamedPropCache);
			}
		}

		TEST_METHOD(Test_Match)
		{
			// Test all forms of match
			Assert::AreEqual(true, formStorage1->match(formStorage1, true, true));
			Assert::AreEqual(true, formStorage1->match(formStorage1, false, true));
			Assert::AreEqual(true, formStorage1->match(formStorage1, true, false));
			Assert::AreEqual(true, formStorage1->match(formStorage1, false, false));

			// Odd comparisons
			Assert::AreEqual(
				true, formStorage1->match(cache::namedPropCacheEntry::make(&formStorageID, 0x1111, sig1), true, true));
			Assert::AreEqual(
				true, formStorage1->match(cache::namedPropCacheEntry::make(&formStorageID, 0x1112, sig1), false, true));
			Assert::AreEqual(
				true,
				formStorage1->match(cache::namedPropCacheEntry::make(&pageDirStreamID, 0x1111, sig1), true, false));
			Assert::AreEqual(
				true,
				formStorage1->match(cache::namedPropCacheEntry::make(&formStorageName, 0x1111, sig1), true, false));
			Assert::AreEqual(
				false,
				formStorage1->match(cache::namedPropCacheEntry::make(&formStorageName, 0x1111, sig1), true, true));

			// Should fail
			Assert::AreEqual(
				false, formStorage1->match(cache::namedPropCacheEntry::make(&formStorageID, 0x1110, sig1), true, true));
			Assert::AreEqual(
				false,
				formStorage1->match(cache::namedPropCacheEntry::make(&pageDirStreamID, 0x1111, sig1), true, true));
			Assert::AreEqual(false, formStorage1->match(nullptr, true, true));
			Assert::AreEqual(false, formStorage1->match(formStorage2, true, true));
			Assert::AreEqual(false, formStorage1->match(formStorageLog, true, true));

			// Compare given a signature, MAPINAMEID
			// _Check_return_ bool match(_In_ const std::vector<BYTE>& _sig, _In_ const MAPINAMEID& _mapiNameId) const;
			Assert::AreEqual(true, formStorage1->match(sig1, formStorageID));
			Assert::AreEqual(false, formStorage1->match(sig2, formStorageID));
			Assert::AreEqual(false, formStorage1->match(sig1, formStorageName));
			Assert::AreEqual(false, formStorage1->match(sig1, formStorageIDLog));
			Assert::AreEqual(false, formStorage1->match(sig1, pageDirStreamID));
			Assert::AreEqual(true, formStorageProp->match(sig1, formStorageName));
			Assert::AreEqual(false, formStorageProp->match(sig1, formStorageName2));

			// Compare given a signature and property ID (ulPropID)
			// _Check_return_ bool match(_In_ const std::vector<BYTE>& _sig, ULONG _ulPropID) const;
			Assert::AreEqual(true, formStorage1->match(sig1, 0x1111));
			Assert::AreEqual(false, formStorage1->match(sig1, 0x1112));
			Assert::AreEqual(false, formStorage1->match(sig2, 0x1111));

			// Compare given a id, MAPINAMEID
			// _Check_return_ bool match(ULONG _ulPropID, _In_ const MAPINAMEID& _mapiNameId) const noexcept;
			Assert::AreEqual(true, formStorage1->match(0x1111, formStorageID));
			Assert::AreEqual(false, formStorage1->match(0x1112, formStorageID));
			Assert::AreEqual(false, formStorage1->match(0x1111, pageDirStreamID));
			Assert::AreEqual(false, formStorage1->match(0x1111, formStorageName));
			Assert::AreEqual(false, formStorage1->match(0x1111, formStorageIDLog));
			Assert::AreEqual(true, formStorageProp->match(0x1111, formStorageName));
			Assert::AreEqual(false, formStorageProp->match(0x1111, formStorageName2));

			// String prop
			Assert::AreEqual(true, formStorageProp->match(formStorageProp, true, true));
			Assert::AreEqual(false, formStorageProp->match(formStorageProp1, true, true));
		}

		TEST_METHOD(Test_Cache)
		{
			cache::namedPropCache::add(ids1, sig1); // Add prop1, prop2 with signature
			cache::namedPropCache::add(ids1, {}); // Try to add them again without signature - this is a no-op
			cache::namedPropCache::add(ids2, {}); // Again, adding without a signature is a no op

			Assert::AreEqual(true, cache::namedPropCache::find(prop1, true, true)->match(prop1, true, true));
			Assert::AreEqual(true, cache::namedPropCache::find(prop2, true, true)->match(prop2, true, true));
			Assert::AreEqual(
				true,
				cache::namedPropCache::find(prop3, true, true)
					->match(cache::namedPropCacheEntry::empty(), true, true)); // Shouldn't find prop3 in the cache
			Assert::AreEqual(true, cache::namedPropCache::find(0x1111, formStorageID)->match(prop1, true, true));
			Assert::AreEqual(true, cache::namedPropCache::find(sig1, 0x1111)->match(prop1, true, true));
			Assert::AreEqual(true, cache::namedPropCache::find(sig1, formStorageID)->match(prop1, true, true));

			Assert::AreEqual(
				true,
				cache::namedPropCache::find(0x1110, formStorageID)
					->match(cache::namedPropCacheEntry::empty(), true, true));

			Assert::AreEqual(false, cache::namedPropCache::find(0x1110, formStorageID)->match(prop1, true, true));
			Assert::AreEqual(false, cache::namedPropCache::find(sig2, 0x1111)->match(prop1, true, true));
			Assert::AreEqual(false, cache::namedPropCache::find(sig2, formStorageID)->match(prop1, true, true));

			// None of these should match prop3 since we never cached it!
			Assert::AreEqual(
				true,
				cache::namedPropCache::find(0x3333, pageDirStreamID)
					->match(cache::namedPropCacheEntry::empty(), true, true));
			Assert::AreEqual(
				true, cache::namedPropCache::find({}, 0x3333)->match(cache::namedPropCacheEntry::empty(), true, true));
			Assert::AreEqual(
				true,
				cache::namedPropCache::find(std::vector<BYTE>{}, pageDirStreamID)
					->match(cache::namedPropCacheEntry::empty(), true, true));

			cache::namedPropCache::add(ids2, sig2); // Now add prop3 with a signature and our lookups should work
			Assert::AreEqual(
				true,
				cache::namedPropCache::find(prop3, true, true)
					->match(prop3, true, true)); // Shouldn't find prop3 in the cache
			Assert::AreEqual(true, cache::namedPropCache::find(0x3333, pageDirStreamID)->match(prop3, true, true));
			Assert::AreEqual(true, cache::namedPropCache::find({}, 0x3333)->match(prop3, true, true));
			Assert::AreEqual(
				true, cache::namedPropCache::find(std::vector<BYTE>{}, pageDirStreamID)->match(prop3, true, true));
		}

		TEST_METHOD(Test_Valid)
		{
			Assert::AreEqual(false, cache::namedPropCacheEntry::valid(cache::namedPropCacheEntry::empty()));
			Assert::AreEqual(
				true,
				cache::namedPropCacheEntry::valid(cache::namedPropCacheEntry::make(&formStorageName, 0x1111, sig1)));
			Assert::AreEqual(
				true, cache::namedPropCacheEntry::valid(cache::namedPropCacheEntry::make(&formStorageName, 0x1111)));
			Assert::AreEqual(
				false, cache::namedPropCacheEntry::valid(cache::namedPropCacheEntry::make(&formStorageName, 0)));
			Assert::AreEqual(
				false, cache::namedPropCacheEntry::valid(cache::namedPropCacheEntry::make(nullptr, 0x1111)));
			Assert::AreEqual(false, cache::namedPropCacheEntry::valid(cache::namedPropCacheEntry::make(nullptr, 0)));
			Assert::AreEqual(false, cache::namedPropCacheEntry::valid(nullptr));
			Assert::AreEqual(
				false,
				cache::namedPropCacheEntry::valid(cache::namedPropCacheEntry::make(&formStorageName, PT_ERROR, sig1)));
		}

		TEST_METHOD(Test_Names)
		{
			cache::namedPropCache::add(ids1, sig1);
			cache::namedPropCache::add(ids2, {});

			Assert::AreEqual(false, cache::namedPropCacheEntry::valid(cache::namedPropCacheEntry::empty()));
			const auto sig1bin = SBinary{static_cast<ULONG>(sig1.size()), const_cast<BYTE*>(sig1.data())};
			const auto name1 = cache::NameIDToStrings(0x1111, nullptr, &formStorageID, &sig1bin, false);
			Assert::AreEqual(name1.name, std::wstring{L"id: 0x850F=34063 = dispidFormStorage"});
			Assert::AreEqual(name1.guid, std::wstring{L"{00062008-0000-0000-C000-000000000046} = PSETID_Common"});
			Assert::AreEqual(name1.dasl, std::wstring{L"id/{00062008-0000-0000-C000-000000000046}/850F1111"});
			Assert::AreEqual(name1.bestPidLid, std::wstring{L"dispidFormStorage"});

			const auto name2 = cache::NameIDToStrings(0x1111, nullptr, {}, &sig1bin, false);
			Assert::AreEqual(name2.name, std::wstring{L""});
			Assert::AreEqual(name2.guid, std::wstring{L""});
			Assert::AreEqual(name2.dasl, std::wstring{L""});
			Assert::AreEqual(name2.bestPidLid, std::wstring{L""});

			const auto propNames = cache::NameIDToPropNames(&propDefStream);
			Assert::AreEqual(propNames[0], std::wstring{L"PidLidPropertyDefinitionStream"});
			Assert::AreEqual(propNames[1], std::wstring{L"dispidPropDefStream"});

			const auto propNames2 = cache::NameIDToPropNames({});
			Assert::AreEqual(propNames2.empty(), true);
		}

		TEST_METHOD(Test_String)
		{
			Assert::AreEqual(
				std::wstring(L"ulKind=MNID_ID, lID=850F, guid={00062008-0000-0000-C000-000000000046} = PSETID_Common"),
				strings::MAPINAMEIDToString(formStorageID));
			Assert::AreEqual(
				std::wstring(L"ulKind=MNID_STRING, lpwstrName='name', guid={00062008-0000-0000-C000-000000000046} = "
							 L"PSETID_Common"),
				strings::MAPINAMEIDToString(formStorageName));
			const auto badID = MAPINAMEID{const_cast<LPGUID>(&guid::PSETID_Common), 3, dispidFormStorage};
			Assert::AreEqual(std::wstring(L""), strings::MAPINAMEIDToString(badID));
		}
	};
} // namespace namedproptest


text:
This element installs the Keystone service from git.

Upon booting the image, the service should be running on port 5000.

Configuration
-------------

Keystone requires the keystone.db key to be set via Heat Metadata.

    keystone:
      db: "mysql://keyuser:keypass@localhost/keystone"

NOTE: Token cleanup will cause poor performance until the upstream
bug (https://bugs.launchpad.net/keystone/+bug/1188378) is fixed. This
problem is fixed in some distributions (Ubuntu, RHEL, CentOS) where
percona-toolkit's is supported and used to clean out the table on mysql.
http://www.percona.com/mysql-support/policies/percona-toolkit-supported-platforms-and-versions

redpajama_set_name: RedPajamaGithub


text:
from healthvaultlib.utils.xmlutils import XmlUtils

class Instance:

    def __init__(self, instance_xml=None):
        self.id = None
        self.name = None
        self.description = None
        self.platform_url = None
        self.shell_url = None

        if instance_xml is not None:
            self.parse_xml(instance_xml)

    def parse_xml(self, instance_xml):
        xmlutils = XmlUtils(instance_xml)
        self.id = xmlutils.get_string_by_xpath('id/text()')
        self.name = xmlutils.get_string_by_xpath('name/text()')
        self.description = xmlutils.get_string_by_xpath('description/text()')
        self.platform_url = xmlutils.get_string_by_xpath('platform-url/text()')
        self.shell_url = xmlutils.get_string_by_xpath('shell-url/text()')


text:
<?php
/**
 * CouchDBAppModel
 *
 * Provides the CouchDB plugin base model to be extended
 *
 * @author maxwellium - https://github.com/maxwellium
 * @package CouchDB
 * @subpackage Model
 * @filesource
 */
class CouchDBAppModel extends AppModel {

/**
 * The db config to be used
 *
 * @var string
 * @access public
 */
	public $useDbConfig = 'couchDB';

/**
 * The database to be used
 *
 * @var string
 * @access public
 */
	public $database = null;

/**
 * The current revision of the couch document
 *
 * @var string
 * @access public
 */
	public $_rev = null;

/**
 * The Model's primary key
 *
 * @var string
 * @access public
 */
	public $primaryKey = '_id';

/**
 * The revision key
 *
 * @var string
 * @access public
 */
	public $revisionKey = '_rev';

/**
 * (non-PHPdoc)
 * @see Model::schema()
 */
	public function schema($field = false) {
		$this->_schema = array_flip(array_keys($this->validate));

		if (isset($this->data[$this->alias]) && is_array($this->data[$this->alias])) {
			$this->_schema = array_merge(
				$this->_schema,
				array_flip(array_keys($this->data[$this->alias])));
		}

		if (is_string($field)) {
			if (isset($this->_schema[$field])) {
				return $this->_schema[$field];
			}
			return null;
    	}

		return $this->_schema;
	}

/**
 * (non-PHPdoc)
 * @see Model::hasField()
 */
	public function hasField($name, $checkVirtual = false) {
		if (is_array($name)) {
			foreach ($name as $n) {
				if ($this->hasField($n, $checkVirtual)) {
					return $n;
				}
			}
			return false;
		}

		if ($checkVirtual && !empty($this->virtualFields)) {
			if ($this->isVirtualField($name)) {
				return true;
			}
		}

		// this is the change: rebuilding schema from data everytime so all fields are submitted
		$this->schema();

		if ($this->_schema != null) {
			return isset($this->_schema[$name]);
		}

		return false;
	}

/**
 * (non-PHPdoc)
 * @see Model::_findCount()
 */
	protected function _findCount($state, $query, $results = array()) {
		if ($state === 'before') {
			$query['order'] = false;
			$query['fields'] = 'count';
			return $query;
		} elseif ($state === 'after') {
			foreach (array(0, $this->alias) as $key) {
				if (isset($results[0][$key]['count'])) {
					if (($count = count($results)) > 1) {
						return $count;
					}
					return intval($results[0][$key]['count']);
				}
			}
			return false;
		}
	}

/**
 * Updates the revision to the latest one
 *
 * @return void
 * @access public
 *
 */
	public function updateRevision() {
		if (!is_array($this->data)) {
			$this->data = array();
		}
		$this->data[$this->revisionKey] = $this->getDataSource()->getRevision($this);
	}

}


text:
'use strict';

var App = require('../../app');
var Backbone = require('backbone');
var ExamsView = require('./ExamsView');
var ExportView = require('./ExportView');
var Marionette = require('backbone.marionette');
var SelectView = require('./SelectView');
var SemesterSelectorView = require('./SemesterSelectorView');
var SharedTimetableControlsView = require('./SharedTimetableControlsView');
var ShowHideView = require('./ShowHideView');
var TimetableView = require('./TableView');
var TipsView = require('./TipsView');
var UrlSharingView = require('./UrlSharingView');
var config = require('../../common/config');
var template = require('../templates/timetable.hbs');
var tips = require('../tips.json');

module.exports = Marionette.LayoutView.extend({
  template: template,

  regions: {
    examsRegion: '#exam-timetable',
    exportRegion: '.export-region',
    selectRegion: '.select-region',
    semesterSelectorRegion: '.semester-selector-region',
    sharedTimetableControlsRegion: '.shared-timetable-controls-region',
    showHideRegion: '.show-hide-region',
    timetableRegion: '#timetable-wrapper',
    tipsRegion: '.tips-region',
    urlSharingRegion: '.url-sharing-region'
  },

  initialize: function (options) {
    this.academicYear = options.academicYear;
    this.semester = options.semester;
  },

  onShow: function() {
    this.selectedModules = App.request('selectedModules', this.semester);
    this.timetable = this.selectedModules.timetable;

    this.listenTo(this.selectedModules, 'add remove', this.modulesChanged);
    this.listenTo(this.timetable, 'change', this.modulesChanged);

    this.examsRegion.show(new ExamsView({collection: this.selectedModules.exams}));
    this.exportRegion.show(new ExportView({
      academicYear: this.academicYear,
      collection: this.selectedModules,
      exams: this.selectedModules.exams,
      semester: this.semester
    }));
    this.selectRegion.show(new SelectView({
      semester: this.semester
    }));
    if (this.selectedModules.shared) {
      this.sharedTimetableControlsRegion.show(new SharedTimetableControlsView({
        collection: this.selectedModules
      }));
    }
    this.semesterSelectorRegion.show(new SemesterSelectorView({
      semester: this.semester
    }));
    this.showHideRegion.show(new ShowHideView());
    this.timetableRegion.show(new TimetableView({collection: this.timetable}));

    var tipsModel = new Backbone.Model({tips: tips});
    this.tipsRegion.show(new TipsView({model: tipsModel}));

    this.urlSharingRegion.show(new UrlSharingView({
      collection: this.selectedModules
    }));
    this.modulesChanged(null, null, {replace: true});

    // Init FB Page Plugin if FB JS SDK has been loaded.
    if (window.FB) {
      FB.XFBML.parse();
    } else {
      (function(d, s, id) {
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) return;
        js = d.createElement(s); js.id = id;
        js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.3&appId=1524196174461544";
        fjs.parentNode.insertBefore(js, fjs);
      }(document, 'script', 'facebook-jssdk'));
    }
  },

  modulesChanged: function (model, collection, options) {
    if (this.selectedModules.length) {
      Backbone.history.navigate(config.semTimetableFragment(this.semester) +
        '?' + this.selectedModules.toQueryString(), options);
    } else {
      Backbone.history.navigate(config.semTimetableFragment(this.semester), options);
    }
  }
});


text:
package org.apache.curator.framework.recipes.cache;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.listen.ListenerContainer;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.curator.utils.CloseableExecutorService;
import org.apache.curator.utils.EnsurePath;
import org.apache.curator.utils.ThreadUtils;
import org.apache.curator.utils.ZKPaths;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.curator.utils.PathUtils;

/**
 *
A utility that attempts to keep all data from all children of a ZK path locally cached. This class
 * will watch the ZK path, respond to update/create/delete events, pull down the data, etc. You can
 * register a listener that will get notified when changes occur.

 *

 *
IMPORTANT - it's not possible to stay transactionally in sync. Users of this class must
 * be prepared for false-positives and false-negatives. Additionally, always use the version number
 * when updating data to avoid overwriting another process' change.

 */
@SuppressWarnings("NullableProblems")
public class PathChildrenCache implements Closeable
{
    private final Logger log = LoggerFactory.getLogger(getClass());
    private final CuratorFramework client;
    private final String path;
    private final CloseableExecutorService executorService;
    private final boolean cacheData;
    private final boolean dataIsCompressed;
    private final EnsurePath ensurePath;
    private final ListenerContainer<PathChildrenCacheListener> listeners = new ListenerContainer<PathChildrenCacheListener>();
    private final ConcurrentMap<String, ChildData> currentData = Maps.newConcurrentMap();
    private final AtomicReference<Map<String, ChildData>> initialSet = new AtomicReference<Map<String, ChildData>>();
    private final Set<Operation> operationsQuantizer = Sets.newSetFromMap(Maps.<Operation, Boolean>newConcurrentMap());
    private final AtomicReference<State> state = new AtomicReference<State>(State.LATENT);

    private enum State
    {
        LATENT,
        STARTED,
        CLOSED
    }

    private static final ChildData NULL_CHILD_DATA = new ChildData("/", null, null);

    private static final boolean USE_EXISTS = Boolean.getBoolean("curator-path-children-cache-use-exists");

    private volatile Watcher childrenWatcher = new Watcher()
    {
        @Override
        public void process(WatchedEvent event)
        {
            offerOperation(new RefreshOperation(PathChildrenCache.this, RefreshMode.STANDARD));
        }
    };

    private volatile Watcher dataWatcher = new Watcher()
    {
        @Override
        public void process(WatchedEvent event)
        {
            try
            {
                if ( event.getType() == Event.EventType.NodeDeleted )
                {
                    remove(event.getPath());
                }
                else if ( event.getType() == Event.EventType.NodeDataChanged )
                {
                    offerOperation(new GetDataOperation(PathChildrenCache.this, event.getPath()));
                }
            }
            catch ( Exception e )
            {
                handleException(e);
            }
        }
    };

    @VisibleForTesting
    volatile Exchanger<Object> rebuildTestExchanger;

    private volatile ConnectionStateListener connectionStateListener = new ConnectionStateListener()
    {
        @Override
        public void stateChanged(CuratorFramework client, ConnectionState newState)
        {
            handleStateChange(newState);
        }
    };
    private static final ThreadFactory defaultThreadFactory = ThreadUtils.newThreadFactory("PathChildrenCache");

    /**
     * @param client the client
     * @param path   path to watch
     * @param mode   caching mode
     * @deprecated use {@link #PathChildrenCache(CuratorFramework, String, boolean)} instead
     */
    @SuppressWarnings("deprecation")
    public PathChildrenCache(CuratorFramework client, String path, PathChildrenCacheMode mode)
    {
        this(client, path, mode != PathChildrenCacheMode.CACHE_PATHS_ONLY, false, new CloseableExecutorService(Executors.newSingleThreadExecutor(defaultThreadFactory), true));
    }

    /**
     * @param client        the client
     * @param path          path to watch
     * @param mode          caching mode
     * @param threadFactory factory to use when creating internal threads
     * @deprecated use {@link #PathChildrenCache(CuratorFramework, String, boolean, ThreadFactory)} instead
     */
    @SuppressWarnings("deprecation")
    public PathChildrenCache(CuratorFramework client, String path, PathChildrenCacheMode mode, ThreadFactory threadFactory)
    {
        this(client, path, mode != PathChildrenCacheMode.CACHE_PATHS_ONLY, false, new CloseableExecutorService(Executors.newSingleThreadExecutor(threadFactory), true));
    }

    /**
     * @param client    the client
     * @param path      path to watch
     * @param cacheData if true, node contents are cached in addition to the stat
     */
    public PathChildrenCache(CuratorFramework client, String path, boolean cacheData)
    {
        this(client, path, cacheData, false, new CloseableExecutorService(Executors.newSingleThreadExecutor(defaultThreadFactory), true));
    }

    /**
     * @param client        the client
     * @param path          path to watch
     * @param cacheData     if true, node contents are cached in addition to the stat
     * @param threadFactory factory to use when creating internal threads
     */
    public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, ThreadFactory threadFactory)
    {
        this(client, path, cacheData, false, new CloseableExecutorService(Executors.newSingleThreadExecutor(threadFactory), true));
    }

    /**
     * @param client           the client
     * @param path             path to watch
     * @param cacheData        if true, node contents are cached in addition to the stat
     * @param dataIsCompressed if true, data in the path is compressed
     * @param threadFactory    factory to use when creating internal threads
     */
    public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, ThreadFactory threadFactory)
    {
        this(client, path, cacheData, dataIsCompressed, new CloseableExecutorService(Executors.newSingleThreadExecutor(threadFactory), true));
    }

    /**
     * @param client           the client
     * @param path             path to watch
     * @param cacheData        if true, node contents are cached in addition to the stat
     * @param dataIsCompressed if true, data in the path is compressed
     * @param executorService  ExecutorService to use for the PathChildrenCache's background thread
     */
    public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, final ExecutorService executorService)
    {
        this(client, path, cacheData, dataIsCompressed, new CloseableExecutorService(executorService));
    }

    /**
     * @param client           the client
     * @param path             path to watch
     * @param cacheData        if true, node contents are cached in addition to the stat
     * @param dataIsCompressed if true, data in the path is compressed
     * @param executorService  Closeable ExecutorService to use for the PathChildrenCache's background thread
     */
    public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, final CloseableExecutorService executorService)
    {
        this.client = client;
        this.path = PathUtils.validatePath(path);
        this.cacheData = cacheData;
        this.dataIsCompressed = dataIsCompressed;
        this.executorService = executorService;
        ensurePath = client.newNamespaceAwareEnsurePath(path);
    }

    /**
     * Start the cache. The cache is not started automatically. You must call this method.
     *
     * @throws Exception errors
     */
    public void start() throws Exception
    {
        start(StartMode.NORMAL);
    }

    /**
     * Same as {@link #start()} but gives the option of doing an initial build
     *
     * @param buildInitial if true, {@link #rebuild()} will be called before this method
     *                     returns in order to get an initial view of the node; otherwise,
     *                     the cache will be initialized asynchronously
     * @throws Exception errors
     * @deprecated use {@link #start(StartMode)}
     */
    public void start(boolean buildInitial) throws Exception
    {
        start(buildInitial ? StartMode.BUILD_INITIAL_CACHE : StartMode.NORMAL);
    }

    /**
     * Method of priming cache on {@link PathChildrenCache#start(StartMode)}
     */
    public enum StartMode
    {
        /**
         * The cache will be primed (in the background) with initial values.
         * Events for existing and new nodes will be posted.
         */
        NORMAL,

        /**
         * The cache will be primed (in the foreground) with initial values.
         * {@link PathChildrenCache#rebuild()} will be called before
         * the {@link PathChildrenCache#start(StartMode)} method returns
         * in order to get an initial view of the node.
         */
        BUILD_INITIAL_CACHE,

        /**
         * After cache is primed with initial values (in the background) a
         * {@link PathChildrenCacheEvent.Type#INITIALIZED} will be posted.
         */
        POST_INITIALIZED_EVENT
    }

    /**
     * Start the cache. The cache is not started automatically. You must call this method.
     *
     * @param mode Method for priming the cache
     * @throws Exception errors
     */
    public void start(StartMode mode) throws Exception
    {
        Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "already started");
        mode = Preconditions.checkNotNull(mode, "mode cannot be null");

        client.getConnectionStateListenable().addListener(connectionStateListener);

        switch ( mode )
        {
            case NORMAL:
            {
                offerOperation(new RefreshOperation(this, RefreshMode.STANDARD));
                break;
            }

            case BUILD_INITIAL_CACHE:
            {
                rebuild();
                break;
            }

            case POST_INITIALIZED_EVENT:
            {
                initialSet.set(Maps.<String, ChildData>newConcurrentMap());
                offerOperation(new RefreshOperation(this, RefreshMode.POST_INITIALIZED));
                break;
            }
        }
    }

    /**
     * NOTE: this is a BLOCKING method. Completely rebuild the internal cache by querying
     * for all needed data WITHOUT generating any events to send to listeners.
     *
     * @throws Exception errors
     */
    public void rebuild() throws Exception
    {
        Preconditions.checkState(!executorService.isShutdown(), "cache has been closed");

        ensurePath.ensure(client.getZookeeperClient());

        clear();

        List<String> children = client.getChildren().forPath(path);
        for ( String child : children )
        {
            String fullPath = ZKPaths.makePath(path, child);
            internalRebuildNode(fullPath);

            if ( rebuildTestExchanger != null )
            {
                rebuildTestExchanger.exchange(new Object());
            }
        }

        // this is necessary so that any updates that occurred while rebuilding are taken
        offerOperation(new RefreshOperation(this, RefreshMode.FORCE_GET_DATA_AND_STAT));
    }

    /**
     * NOTE: this is a BLOCKING method. Rebuild the internal cache for the given node by querying
     * for all needed data WITHOUT generating any events to send to listeners.
     *
     * @param fullPath full path of the node to rebuild
     * @throws Exception errors
     */
    public void rebuildNode(String fullPath) throws Exception
    {
        Preconditions.checkArgument(ZKPaths.getPathAndNode(fullPath).getPath().equals(path), "Node is not part of this cache: " + fullPath);
        Preconditions.checkState(!executorService.isShutdown(), "cache has been closed");

        ensurePath.ensure(client.getZookeeperClient());
        internalRebuildNode(fullPath);

        // this is necessary so that any updates that occurred while rebuilding are taken
        // have to rebuild entire tree in case this node got deleted in the interim
        offerOperation(new RefreshOperation(this, RefreshMode.FORCE_GET_DATA_AND_STAT));
    }

    /**
     * Close/end the cache
     *
     * @throws IOException errors
     */
    @Override
    public void close() throws IOException
    {
        if ( state.compareAndSet(State.STARTED, State.CLOSED) )
        {
            client.getConnectionStateListenable().removeListener(connectionStateListener);
            listeners.clear();
            executorService.close();
            client.clearWatcherReferences(childrenWatcher);
            client.clearWatcherReferences(dataWatcher);

            // TODO
            // This seems to enable even more GC - I'm not sure why yet - it
            // has something to do with Guava's cache and circular references
            connectionStateListener = null;
            childrenWatcher = null;
            dataWatcher = null;
        }
    }

    /**
     * Return the cache listenable
     *
     * @return listenable
     */
    public ListenerContainer<PathChildrenCacheListener> getListenable()
    {
        return listeners;
    }

    /**
     * Return the current data. There are no guarantees of accuracy. This is
     * merely the most recent view of the data. The data is returned in sorted order.
     *
     * @return list of children and data
     */
    public List<ChildData> getCurrentData()
    {
        return ImmutableList.copyOf(Sets.<ChildData>newTreeSet(currentData.values()));
    }

    /**
     * Return the current data for the given path. There are no guarantees of accuracy. This is
     * merely the most recent view of the data. If there is no child with that path, null
     * is returned.
     *
     * @param fullPath full path to the node to check
     * @return data or null
     */
    public ChildData getCurrentData(String fullPath)
    {
        return currentData.get(fullPath);
    }

    /**
     * As a memory optimization, you can clear the cached data bytes for a node. Subsequent
     * calls to {@link ChildData#getData()} for this node will return null.
     *
     * @param fullPath the path of the node to clear
     */
    public void clearDataBytes(String fullPath)
    {
        clearDataBytes(fullPath, -1);
    }

    /**
     * As a memory optimization, you can clear the cached data bytes for a node. Subsequent
     * calls to {@link ChildData#getData()} for this node will return null.
     *
     * @param fullPath  the path of the node to clear
     * @param ifVersion if non-negative, only clear the data if the data's version matches this version
     * @return true if the data was cleared
     */
    public boolean clearDataBytes(String fullPath, int ifVersion)
    {
        ChildData data = currentData.get(fullPath);
        if ( data != null )
        {
            if ( (ifVersion < 0) || (ifVersion == data.getStat().getVersion()) )
            {
                data.clearData();
                return true;
            }
        }
        return false;
    }

    /**
     * Clear out current data and begin a new query on the path
     *
     * @throws Exception errors
     */
    public void clearAndRefresh() throws Exception
    {
        currentData.clear();
        offerOperation(new RefreshOperation(this, RefreshMode.STANDARD));
    }

    /**
     * Clears the current data without beginning a new query and without generating any events
     * for listeners.
     */
    public void clear()
    {
        currentData.clear();
    }

    enum RefreshMode
    {
        STANDARD,
        FORCE_GET_DATA_AND_STAT,
        POST_INITIALIZED
    }

    void refresh(final RefreshMode mode) throws Exception
    {
        ensurePath.ensure(client.getZookeeperClient());

        final BackgroundCallback callback = new BackgroundCallback()
        {
            @Override
            public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
            {
                if (PathChildrenCache.this.state.get().equals(State.CLOSED)) {
                    // This ship is closed, don't handle the callback
                    return;
                }
                if ( event.getResultCode() == KeeperException.Code.OK.intValue() )
                {
                    processChildren(event.getChildren(), mode);
                }
            }
        };

        client.getChildren().usingWatcher(childrenWatcher).inBackground(callback).forPath(path);
    }

    void callListeners(final PathChildrenCacheEvent event)
    {
        listeners.forEach
            (
                new Function<PathChildrenCacheListener, Void>()
                {
                    @Override
                    public Void apply(PathChildrenCacheListener listener)
                    {
                        try
                        {
                            listener.childEvent(client, event);
                        }
                        catch ( Exception e )
                        {
                            handleException(e);
                        }
                        return null;
                    }
                }
            );
    }

    void getDataAndStat(final String fullPath) throws Exception
    {
        BackgroundCallback callback = new BackgroundCallback()
        {
            @Override
            public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
            {
                applyNewData(fullPath, event.getResultCode(), event.getStat(), cacheData ? event.getData() : null);
            }
        };

        if ( USE_EXISTS && !cacheData )
        {
            client.checkExists().usingWatcher(dataWatcher).inBackground(callback).forPath(fullPath);
        }
        else
        {
            // always use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak
            if ( dataIsCompressed && cacheData )
            {
                client.getData().decompressed().usingWatcher(dataWatcher).inBackground(callback).forPath(fullPath);
            }
            else
            {
                client.getData().usingWatcher(dataWatcher).inBackground(callback).forPath(fullPath);
            }
        }
    }

    /**
     * Default behavior is just to log the exception
     *
     * @param e the exception
     */
    protected void handleException(Throwable e)
    {
        log.error("", e);
    }

    @VisibleForTesting
    protected void remove(String fullPath)
    {
        ChildData data = currentData.remove(fullPath);
        if ( data != null )
        {
            offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CHILD_REMOVED, data)));
        }

        Map<String, ChildData> localInitialSet = initialSet.get();
        if ( localInitialSet != null )
        {
            localInitialSet.remove(fullPath);
            maybeOfferInitializedEvent(localInitialSet);
        }
    }

    private void internalRebuildNode(String fullPath) throws Exception
    {
        if ( cacheData )
        {
            try
            {
                Stat stat = new Stat();
                byte[] bytes = dataIsCompressed ? client.getData().decompressed().storingStatIn(stat).forPath(fullPath) : client.getData().storingStatIn(stat).forPath(fullPath);
                currentData.put(fullPath, new ChildData(fullPath, stat, bytes));
            }
            catch ( KeeperException.NoNodeException ignore )
            {
                // node no longer exists - remove it
                currentData.remove(fullPath);
            }
        }
        else
        {
            Stat stat = client.checkExists().forPath(fullPath);
            if ( stat != null )
            {
                currentData.put(fullPath, new ChildData(fullPath, stat, null));
            }
            else
            {
                // node no longer exists - remove it
                currentData.remove(fullPath);
            }
        }
    }

    private void handleStateChange(ConnectionState newState)
    {
        switch ( newState )
        {
        case SUSPENDED:
        {
            offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED, null)));
            break;
        }

        case LOST:
        {
            offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CONNECTION_LOST, null)));
            break;
        }

        case RECONNECTED:
        {
            try
            {
                offerOperation(new RefreshOperation(this, RefreshMode.FORCE_GET_DATA_AND_STAT));
                offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED, null)));
            }
            catch ( Exception e )
            {
                handleException(e);
            }
            break;
        }
        }
    }

    private void processChildren(List<String> children, RefreshMode mode) throws Exception
    {
        Set<String> removedNodes = Sets.newHashSet(currentData.keySet());
        for ( String child : children ) {
            removedNodes.remove(ZKPaths.makePath(path, child));
        }

        for ( String fullPath : removedNodes )
        {
            remove(fullPath);
        }

        for ( String name : children )
        {
            String fullPath = ZKPaths.makePath(path, name);

            if ( (mode == RefreshMode.FORCE_GET_DATA_AND_STAT) || !currentData.containsKey(fullPath) )
            {
                getDataAndStat(fullPath);
            }

            updateInitialSet(name, NULL_CHILD_DATA);
        }
        maybeOfferInitializedEvent(initialSet.get());
    }

    private void applyNewData(String fullPath, int resultCode, Stat stat, byte[] bytes)
    {
        if ( resultCode == KeeperException.Code.OK.intValue() ) // otherwise - node must have dropped or something - we should be getting another event
        {
            ChildData data = new ChildData(fullPath, stat, bytes);
            ChildData previousData = currentData.put(fullPath, data);
            if ( previousData == null ) // i.e. new
            {
                offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CHILD_ADDED, data)));
            }
            else if ( previousData.getStat().getVersion() != stat.getVersion() )
            {
                offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CHILD_UPDATED, data)));
            }
            updateInitialSet(ZKPaths.getNodeFromPath(fullPath), data);
        }
    }

    private void updateInitialSet(String name, ChildData data)
    {
        Map<String, ChildData> localInitialSet = initialSet.get();
        if ( localInitialSet != null )
        {
            localInitialSet.put(name, data);
            maybeOfferInitializedEvent(localInitialSet);
        }
    }

    private void maybeOfferInitializedEvent(Map<String, ChildData> localInitialSet)
    {
        if ( !hasUninitialized(localInitialSet) )
        {
            // all initial children have been processed - send initialized message

            if ( initialSet.getAndSet(null) != null )   // avoid edge case - don't send more than 1 INITIALIZED event
            {
                final List<ChildData> children = ImmutableList.copyOf(localInitialSet.values());
                PathChildrenCacheEvent event = new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.INITIALIZED, null)
                {
                    @Override
                    public List<ChildData> getInitialData()
                    {
                        return children;
                    }
                };
                offerOperation(new EventOperation(this, event));
            }
        }
    }

    private boolean hasUninitialized(Map<String, ChildData> localInitialSet)
    {
        if ( localInitialSet == null )
        {
            return false;
        }

        Map<String, ChildData> uninitializedChildren = Maps.filterValues
            (
                localInitialSet,
                new Predicate<ChildData>()
                {
                    @Override
                    public boolean apply(ChildData input)
                    {
                        return (input == NULL_CHILD_DATA);  // check against ref intentional
                    }
                }
            );
        return (uninitializedChildren.size() != 0);
    }

    void offerOperation(final Operation operation)
    {
        if ( operationsQuantizer.add(operation) )
        {
            submitToExecutor
            (
                new Runnable()
                {
                    @Override
                    public void run()
                    {
                        try
                        {
                            operationsQuantizer.remove(operation);
                            operation.invoke();
                        }
                        catch ( InterruptedException e )
                        {
                            //We expect to get interrupted during shutdown,
                            //so just ignore these events
                            if ( state.get() != State.CLOSED )
                            {
                                handleException(e);
                            }
                            Thread.currentThread().interrupt();
                        }
                        catch ( Exception e )
                        {
                            handleException(e);
                        }
                    }
                }
            );
        }
    }

    /**
     * Submits a runnable to the executor.
     *

     * This method is synchronized because it has to check state about whether this instance is still open.  Without this check
     * there is a race condition with the dataWatchers that get set.  Even after this object is closed() it can still be
     * called by those watchers, because the close() method cannot actually disable the watcher.
     *

     * The synchronization overhead should be minimal if non-existant as this is generally only called from the
     * ZK client thread and will only contend if close() is called in parallel with an update, and that's the exact state
     * we want to protect from.
     *
     * @param command The runnable to run
     */
    private synchronized void submitToExecutor(final Runnable command)
    {
        if ( state.get() == State.STARTED )
        {
            executorService.submit(command);
        }
    }
}