Repository: helix Updated Branches: refs/heads/helix-0.6.x 8cf80a9f8 -> 8ba068e7b
Add support of setting/updating Cluster/Resource/Instance configs in ConfigAccessor. Project: http://git-wip-us.apache.org/repos/asf/helix/repo Commit: http://git-wip-us.apache.org/repos/asf/helix/commit/7c92bf54 Tree: http://git-wip-us.apache.org/repos/asf/helix/tree/7c92bf54 Diff: http://git-wip-us.apache.org/repos/asf/helix/diff/7c92bf54 Branch: refs/heads/helix-0.6.x Commit: 7c92bf543571daac555f5d8b933805dddced7ca5 Parents: 8cf80a9 Author: Lei Xia <[email protected]> Authored: Tue May 23 12:27:23 2017 -0700 Committer: Lei Xia <[email protected]> Committed: Tue May 23 12:27:23 2017 -0700 ---------------------------------------------------------------------- .../java/org/apache/helix/ConfigAccessor.java | 248 +++++++++++++++++++ .../main/java/org/apache/helix/ZNRecord.java | 19 ++ .../java/org/apache/helix/ZNRecordDelta.java | 5 +- .../org/apache/helix/manager/zk/ZKUtil.java | 31 ++- .../org/apache/helix/model/ClusterConfig.java | 15 ++ .../integration/ZkIntegrationTestBase.java | 12 +- .../org/apache/helix/manager/zk/TestZKUtil.java | 113 ++++++--- 7 files changed, 401 insertions(+), 42 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/helix/blob/7c92bf54/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java ---------------------------------------------------------------------- diff --git a/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java b/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java index 34aef49..27a30cb 100644 --- a/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java +++ b/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java @@ -27,11 +27,15 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import org.apache.helix.model.ClusterConfig; import org.apache.helix.model.ConfigScope; import org.apache.helix.model.HelixConfigScope; import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty; import org.apache.helix.manager.zk.ZKUtil; import org.apache.helix.manager.zk.ZkClient; +import org.apache.helix.model.InstanceConfig; +import org.apache.helix.model.ResourceConfig; +import org.apache.helix.model.builder.HelixConfigScopeBuilder; import org.apache.helix.util.StringTemplate; import org.apache.log4j.Logger; @@ -509,4 +513,248 @@ public class ConfigAccessor { } return retKeys; } + + private ZNRecord getConfigZnRecord(HelixConfigScope scope) { + String clusterName = scope.getClusterName(); + if (!ZKUtil.isClusterSetup(clusterName, zkClient)) { + throw new HelixException("fail to get configs. cluster " + clusterName + " is not setup yet"); + } + + return zkClient.readData(scope.getZkPath(), true); + } + + /** + * Get ClusterConfig of the given cluster. + * + * @param clusterName + * + * @return + */ + public ClusterConfig getClusterConfig(String clusterName) { + if (!ZKUtil.isClusterSetup(clusterName, zkClient)) { + throw new HelixException("fail to get config. cluster: " + clusterName + " is NOT setup."); + } + + HelixConfigScope scope = + new HelixConfigScopeBuilder(ConfigScopeProperty.CLUSTER).forCluster(clusterName).build(); + ZNRecord record = getConfigZnRecord(scope); + + if (record == null) { + LOG.warn("No config found at " + scope.getZkPath()); + return null; + } + + return new ClusterConfig(record); + } + + /** + * Set ClusterConfig of the given cluster. + * The current Cluster config will be replaced with the given clusterConfig. + * WARNING: This is not thread-safe or concurrent updates safe. + * + * @param clusterName + * @param clusterConfig + * + * @return + */ + public void setClusterConfig(String clusterName, ClusterConfig clusterConfig) { + updateClusterConfig(clusterName, clusterConfig, true); + } + + /** + * Update ClusterConfig of the given cluster. + * The value of field in current config will be replaced with the value of the same field in given config if it + * presents. If there is new field in given config but not in current config, the field will be added into + * the current config.. + * The list fields and map fields will be replaced as a single entry. + * + * The current Cluster config will be replaced with the given clusterConfig. + * WARNING: This is not thread-safe or concurrent updates safe. + * + * @param clusterName + * @param clusterConfig + * + * @return + */ + public void updateClusterConfig(String clusterName, ClusterConfig clusterConfig) { + updateClusterConfig(clusterName, clusterConfig, false); + } + + + private void updateClusterConfig(String clusterName, ClusterConfig clusterConfig, boolean overwrite) { + if (!ZKUtil.isClusterSetup(clusterName, zkClient)) { + throw new HelixException("fail to update config. cluster: " + clusterName + " is NOT setup."); + } + + HelixConfigScope scope = + new HelixConfigScopeBuilder(ConfigScopeProperty.CLUSTER).forCluster(clusterName).build(); + String zkPath = scope.getZkPath(); + + if (overwrite) { + ZKUtil.createOrReplace(zkClient, zkPath, clusterConfig.getRecord(), true); + } else { + ZKUtil.createOrUpdate(zkClient, zkPath, clusterConfig.getRecord(), true, true); + } + } + + /** + * Get resource config for given resource in given cluster. + * + * @param clusterName + * @param resourceName + * + * @return + */ + public ResourceConfig getResourceConfig(String clusterName, String resourceName) { + HelixConfigScope scope = + new HelixConfigScopeBuilder(ConfigScopeProperty.RESOURCE).forCluster(clusterName) + .forResource(resourceName).build(); + ZNRecord record = getConfigZnRecord(scope); + + if (record == null) { + LOG.warn("No config found at " + scope.getZkPath()); + return null; + } + + return new ResourceConfig(record); + } + + /** + * Set config of the given resource. + * The current Resource config will be replaced with the given clusterConfig. + * + * WARNING: This is not thread-safe or concurrent updates safe. + * + * @param clusterName + * @param resourceName + * @param resourceConfig + * + * @return + */ + public void setResourceConfig(String clusterName, String resourceName, + ResourceConfig resourceConfig) { + updateResourceConfig(clusterName, resourceName, resourceConfig, true); + } + + /** + * Update ResourceConfig of the given resource. + * The value of field in current config will be replaced with the value of the same field in given config if it + * presents. If there is new field in given config but not in current config, the field will be added into + * the current config.. + * The list fields and map fields will be replaced as a single entry. + * + * The current Cluster config will be replaced with the given clusterConfig. + * WARNING: This is not thread-safe or concurrent updates safe. + * + * @param clusterName + * @param resourceName + * @param resourceConfig + * + * @return + */ + public void updateResourceConfig(String clusterName, String resourceName, + ResourceConfig resourceConfig) { + updateResourceConfig(clusterName, resourceName, resourceConfig, false); + } + + private void updateResourceConfig(String clusterName, String resourceName, + ResourceConfig resourceConfig, boolean overwrite) { + if (!ZKUtil.isClusterSetup(clusterName, zkClient)) { + throw new HelixException("fail to setup config. cluster: " + clusterName + " is NOT setup."); + } + + HelixConfigScope scope = + new HelixConfigScopeBuilder(ConfigScopeProperty.RESOURCE).forCluster(clusterName) + .forResource(resourceName).build(); + String zkPath = scope.getZkPath(); + + if (overwrite) { + ZKUtil.createOrReplace(zkClient, zkPath, resourceConfig.getRecord(), true); + } else { + ZKUtil.createOrUpdate(zkClient, zkPath, resourceConfig.getRecord(), true, true); + } + } + + /** + * Get instance config for given resource in given cluster. + * + * @param clusterName + * @param instanceName + * + * @return + */ + public InstanceConfig getInstanceConfig(String clusterName, String instanceName) { + if (!ZKUtil.isInstanceSetup(zkClient, clusterName, instanceName, InstanceType.PARTICIPANT)) { + throw new HelixException( + "fail to get config. instance: " + instanceName + " is NOT setup in cluster: " + + clusterName); + } + + HelixConfigScope scope = + new HelixConfigScopeBuilder(ConfigScopeProperty.PARTICIPANT).forCluster(clusterName) + .forParticipant(instanceName).build(); + ZNRecord record = getConfigZnRecord(scope); + + if (record == null) { + LOG.warn("No config found at " + scope.getZkPath()); + return null; + } + + return new InstanceConfig(record); + } + + /** + * Set config of the given instance config. + * The current instance config will be replaced with the given instanceConfig. + * WARNING: This is not thread-safe or concurrent updates safe. + * + * @param clusterName + * @param instanceName + * @param instanceConfig + * + * @return + */ + public void setInstanceConfig(String clusterName, String instanceName, + InstanceConfig instanceConfig) { + updateInstanceConfig(clusterName, instanceName, instanceConfig, true); + + } + + /** + * Update ResourceConfig of the given resource. The value of field in current config will be + * replaced with the value of the same field in given config if it presents. If there is new field + * in given config but not in current config, the field will be added into the current config.. + * The list fields and map fields will be replaced as a single entry. + * The current Cluster config will be replaced with the given clusterConfig. WARNING: This is not + * thread-safe or concurrent updates safe. + * * + * + * @param clusterName + * @param instanceName + * @param instanceConfig + * + * @return + */ + public void updateInstanceConfig(String clusterName, String instanceName, + InstanceConfig instanceConfig) { + updateInstanceConfig(clusterName, instanceName, instanceConfig, false); + } + + private void updateInstanceConfig(String clusterName, String instanceName, + InstanceConfig instanceConfig, boolean overwrite) { + if (!ZKUtil.isClusterSetup(clusterName, zkClient)) { + throw new HelixException("fail to setup config. cluster: " + clusterName + " is NOT setup."); + } + + HelixConfigScope scope = + new HelixConfigScopeBuilder(ConfigScopeProperty.PARTICIPANT).forCluster(clusterName) + .forParticipant(instanceName).build(); + String zkPath = scope.getZkPath(); + + if (overwrite) { + ZKUtil.createOrReplace(zkClient, zkPath, instanceConfig.getRecord(), true); + } else { + ZKUtil.createOrUpdate(zkClient, zkPath, instanceConfig.getRecord(), true, true); + } + } } http://git-wip-us.apache.org/repos/asf/helix/blob/7c92bf54/helix-core/src/main/java/org/apache/helix/ZNRecord.java ---------------------------------------------------------------------- diff --git a/helix-core/src/main/java/org/apache/helix/ZNRecord.java b/helix-core/src/main/java/org/apache/helix/ZNRecord.java index 3ac9485..52bd5be 100644 --- a/helix-core/src/main/java/org/apache/helix/ZNRecord.java +++ b/helix-core/src/main/java/org/apache/helix/ZNRecord.java @@ -512,7 +512,24 @@ public class ZNRecord { } /** + * Replace functionality is used to update this ZNRecord with the given ZNRecord. The value of a + * field in this record will be replaced with the value of the same field in given record if it + * presents. If there is new field in given ZNRecord but not in this record, add that field into + * this record. The list fields and map fields will be replaced as a single entry. + * + * @param record + */ + public void update(ZNRecord record) { + if (record != null) { + simpleFields.putAll(record.simpleFields); + listFields.putAll(record.listFields); + mapFields.putAll(record.mapFields); + } + } + + /** * Merge in a {@link ZNRecordDelta} corresponding to its merge policy + * * @param delta */ void merge(ZNRecordDelta delta) { @@ -520,6 +537,8 @@ public class ZNRecord { merge(delta.getRecord()); } else if (delta.getMergeOperation() == MergeOperation.SUBTRACT) { subtract(delta.getRecord()); + } else if (delta.getMergeOperation() == MergeOperation.UPDATE) { + update(delta.getRecord()); } } http://git-wip-us.apache.org/repos/asf/helix/blob/7c92bf54/helix-core/src/main/java/org/apache/helix/ZNRecordDelta.java ---------------------------------------------------------------------- diff --git a/helix-core/src/main/java/org/apache/helix/ZNRecordDelta.java b/helix-core/src/main/java/org/apache/helix/ZNRecordDelta.java index eff725d..616e1f5 100644 --- a/helix-core/src/main/java/org/apache/helix/ZNRecordDelta.java +++ b/helix-core/src/main/java/org/apache/helix/ZNRecordDelta.java @@ -28,7 +28,8 @@ public class ZNRecordDelta { */ public enum MergeOperation { ADD, - SUBTRACT + SUBTRACT, + UPDATE }; /** @@ -44,7 +45,7 @@ public class ZNRecordDelta { /** * Initialize the delta with a record and the update mode * @param record - * @param _mergeOperation + * @param mergeOperation */ public ZNRecordDelta(ZNRecord record, MergeOperation mergeOperation) { _record = new ZNRecord(record); http://git-wip-us.apache.org/repos/asf/helix/blob/7c92bf54/helix-core/src/main/java/org/apache/helix/manager/zk/ZKUtil.java ---------------------------------------------------------------------- diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKUtil.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKUtil.java index 9131e5b..2b4cfb2 100644 --- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKUtil.java +++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKUtil.java @@ -196,7 +196,7 @@ public final class ZKUtil { } } - public static void createOrUpdate(ZkClient client, String path, final ZNRecord record, + public static void createOrMerge(ZkClient client, String path, final ZNRecord record, final boolean persistent, final boolean mergeOnUpdate) { int retryCount = 0; while (retryCount < RETRYLIMIT) { @@ -232,6 +232,35 @@ public final class ZKUtil { } } + public static void createOrUpdate(ZkClient client, String path, final ZNRecord record, + final boolean persistent, final boolean mergeOnUpdate) { + int retryCount = 0; + while (retryCount < RETRYLIMIT) { + try { + if (client.exists(path)) { + DataUpdater<ZNRecord> updater = new DataUpdater<ZNRecord>() { + @Override public ZNRecord update(ZNRecord currentData) { + if (currentData != null && mergeOnUpdate) { + currentData.update(record); + return currentData; + } + return record; + } + }; + client.updateDataSerialized(path, updater); + } else { + CreateMode mode = (persistent) ? CreateMode.PERSISTENT : CreateMode.EPHEMERAL; + client.create(path, record, mode); + } + break; + } catch (Exception e) { + retryCount = retryCount + 1; + logger.warn("Exception trying to update " + path + " Exception:" + e.getMessage() + + ". Will retry."); + } + } + } + public static void asyncCreateOrUpdate(ZkClient client, String path, final ZNRecord record, final boolean persistent, final boolean mergeOnUpdate) { try { http://git-wip-us.apache.org/repos/asf/helix/blob/7c92bf54/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java ---------------------------------------------------------------------- diff --git a/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java b/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java index 2be7ee1..3796f36 100644 --- a/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java +++ b/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java @@ -68,6 +68,21 @@ public class ClusterConfig extends HelixProperty { } /** + * Enable/Disable persist best possible assignment in a resource's idealstate. + * + * @return + */ + public void setPersistBestPossibleAssignment(Boolean enable) { + if (enable == null) { + _record.getSimpleFields() + .remove(ClusterConfigProperty.PERSIST_BEST_POSSIBLE_ASSIGNMENT.toString()); + } else { + _record.setBooleanField(ClusterConfigProperty.PERSIST_BEST_POSSIBLE_ASSIGNMENT.toString(), + enable); + } + } + + /** * * @return */ http://git-wip-us.apache.org/repos/asf/helix/blob/7c92bf54/helix-core/src/test/java/org/apache/helix/integration/ZkIntegrationTestBase.java ---------------------------------------------------------------------- diff --git a/helix-core/src/test/java/org/apache/helix/integration/ZkIntegrationTestBase.java b/helix-core/src/test/java/org/apache/helix/integration/ZkIntegrationTestBase.java index 0edd4d3..08a5730 100644 --- a/helix-core/src/test/java/org/apache/helix/integration/ZkIntegrationTestBase.java +++ b/helix-core/src/test/java/org/apache/helix/integration/ZkIntegrationTestBase.java @@ -104,15 +104,11 @@ public class ZkIntegrationTestBase { } protected void enablePersistBestPossibleAssignment(ZkClient zkClient, String clusterName, - Boolean enable) { + Boolean enabled) { ConfigAccessor configAccessor = new ConfigAccessor(zkClient); - HelixConfigScope clusterScope = - new HelixConfigScopeBuilder(HelixConfigScope.ConfigScopeProperty.CLUSTER) - .forCluster(clusterName).build(); - - configAccessor.set(clusterScope, - ClusterConfig.ClusterConfigProperty.PERSIST_BEST_POSSIBLE_ASSIGNMENT.name(), - enable.toString()); + ClusterConfig clusterConfig = configAccessor.getClusterConfig(clusterName); + clusterConfig.setPersistBestPossibleAssignment(enabled); + configAccessor.setClusterConfig(clusterName, clusterConfig); } protected void disableDelayRebalanceInCluster(ZkClient zkClient, String clusterName, http://git-wip-us.apache.org/repos/asf/helix/blob/7c92bf54/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKUtil.java ---------------------------------------------------------------------- diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKUtil.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKUtil.java index ed604fa..19d30a6 100644 --- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKUtil.java +++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKUtil.java @@ -19,16 +19,15 @@ package org.apache.helix.manager.zk; * under the License. */ -import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; - +import java.util.Map; import org.apache.helix.PropertyPathBuilder; -import org.apache.helix.PropertyType; import org.apache.helix.TestHelper; import org.apache.helix.ZNRecord; import org.apache.helix.ZkUnitTestBase; -import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty; import org.apache.log4j.Logger; import org.testng.AssertJUnit; import org.testng.annotations.AfterClass; @@ -42,7 +41,7 @@ public class TestZKUtil extends ZkUnitTestBase { ZkClient _zkClient; @BeforeClass() - public void beforeClass() throws IOException, Exception { + public void beforeClass() throws Exception { _zkClient = new ZkClient(ZK_ADDR); _zkClient.setZkSerializer(new ZNRecordSerializer()); if (_zkClient.exists("/" + clusterName)) { @@ -79,9 +78,7 @@ public class TestZKUtil extends ZkUnitTestBase { List<ZNRecord> list = new ArrayList<ZNRecord>(); list.add(new ZNRecord("id1")); list.add(new ZNRecord("id2")); - String path = - PropertyPathBuilder.getPath(PropertyType.CONFIGS, clusterName, - ConfigScopeProperty.PARTICIPANT.toString()); + String path = PropertyPathBuilder.instanceConfig(clusterName); ZKUtil.createChildren(_zkClient, path, list); list = ZKUtil.getChildren(_zkClient, path); AssertJUnit.assertEquals(2, list.size()); @@ -96,63 +93,117 @@ public class TestZKUtil extends ZkUnitTestBase { @Test() public void testUpdateIfExists() { - String path = - PropertyPathBuilder.getPath(PropertyType.CONFIGS, clusterName, - ConfigScopeProperty.PARTICIPANT.toString(), "id3"); + String path = PropertyPathBuilder.instanceConfig(clusterName, "id3"); ZNRecord record = new ZNRecord("id4"); ZKUtil.updateIfExists(_zkClient, path, record, false); AssertJUnit.assertFalse(_zkClient.exists(path)); _zkClient.createPersistent(path); ZKUtil.updateIfExists(_zkClient, path, record, false); AssertJUnit.assertTrue(_zkClient.exists(path)); - record = _zkClient.<ZNRecord> readData(path); + record = _zkClient.readData(path); AssertJUnit.assertEquals("id4", record.getId()); } @Test() public void testSubtract() { - String path = - PropertyPathBuilder.getPath(PropertyType.CONFIGS, clusterName, - ConfigScopeProperty.PARTICIPANT.toString(), "id5"); + String path = PropertyPathBuilder.instanceConfig(clusterName, "id5"); ZNRecord record = new ZNRecord("id5"); record.setSimpleField("key1", "value1"); _zkClient.createPersistent(path, record); ZKUtil.subtract(_zkClient, path, record); - record = _zkClient.<ZNRecord> readData(path); + record = _zkClient.readData(path); AssertJUnit.assertNull(record.getSimpleField("key1")); } @Test() public void testNullChildren() { - String path = - PropertyPathBuilder.getPath(PropertyType.CONFIGS, clusterName, - ConfigScopeProperty.PARTICIPANT.toString(), "id6"); + String path = PropertyPathBuilder.instanceConfig(clusterName, "id6"); ZKUtil.createChildren(_zkClient, path, (List<ZNRecord>) null); } @Test() - public void testCreateOrUpdate() { - String path = - PropertyPathBuilder.getPath(PropertyType.CONFIGS, clusterName, - ConfigScopeProperty.PARTICIPANT.toString(), "id7"); + public void testCreateOrMerge() { + String path = PropertyPathBuilder.instanceConfig(clusterName, "id7"); ZNRecord record = new ZNRecord("id7"); - ZKUtil.createOrUpdate(_zkClient, path, record, true, true); - record = _zkClient.<ZNRecord> readData(path); - AssertJUnit.assertEquals("id7", record.getId()); + List<String> list = Arrays.asList("value1"); + record.setListField("list", list); + ZKUtil.createOrMerge(_zkClient, path, record, true, true); + record = _zkClient.readData(path); + AssertJUnit.assertEquals(list, record.getListField("list")); + + record = new ZNRecord("id7"); + List<String> list2 = Arrays.asList("value2"); + record.setListField("list", list2); + ZKUtil.createOrMerge(_zkClient, path, record, true, true); + record = _zkClient.readData(path); + AssertJUnit.assertEquals(Arrays.asList("value1", "value2"), record.getListField("list")); + + Map<String, String> map = new HashMap<String, String>() {{put("k1", "v1");}}; + record.setMapField("map", map); + ZKUtil.createOrMerge(_zkClient, path, record, true, true); + record = _zkClient.readData(path); + AssertJUnit.assertEquals(map, record.getMapField("map")); + + record = new ZNRecord("id7"); + Map<String, String> map2 = new HashMap<String, String>() {{put("k2", "v2");}}; + record.setMapField("map", map2); + ZKUtil.createOrMerge(_zkClient, path, record, true, true); + record = _zkClient.readData(path); + AssertJUnit.assertEquals(new HashMap<String, String>() {{ + put("k1", "v1"); + put("k2", "v2"); + }}, record.getMapField("map")); } @Test() public void testCreateOrReplace() { - String path = - PropertyPathBuilder.getPath(PropertyType.CONFIGS, clusterName, - ConfigScopeProperty.PARTICIPANT.toString(), "id8"); + String path = PropertyPathBuilder.instanceConfig(clusterName, "id8"); ZNRecord record = new ZNRecord("id8"); ZKUtil.createOrReplace(_zkClient, path, record, true); - record = _zkClient.<ZNRecord> readData(path); + record = _zkClient.readData(path); AssertJUnit.assertEquals("id8", record.getId()); record = new ZNRecord("id9"); ZKUtil.createOrReplace(_zkClient, path, record, true); - record = _zkClient.<ZNRecord> readData(path); + record = _zkClient.readData(path); AssertJUnit.assertEquals("id9", record.getId()); } + + @Test() + public void testCreateOrUpdate() { + String path = PropertyPathBuilder.instanceConfig(clusterName, "id7"); + ZNRecord record = new ZNRecord("id7"); + ZKUtil.createOrMerge(_zkClient, path, record, true, true); + record = _zkClient.readData(path); + AssertJUnit.assertEquals("id7", record.getId()); + + record = new ZNRecord("id7"); + List<String> list = Arrays.asList("value1", "value2"); + record.setListField("list", list); + ZKUtil.createOrUpdate(_zkClient, path, record, true, true); + record = _zkClient.readData(path); + AssertJUnit.assertEquals(list, record.getListField("list")); + + record = new ZNRecord("id7"); + List<String> list2 = Arrays.asList("value3", "value4"); + record.setListField("list", list2); + ZKUtil.createOrUpdate(_zkClient, path, record, true, true); + record = _zkClient.readData(path); + AssertJUnit.assertEquals(list2, record.getListField("list")); + + + Map<String, String> map = new HashMap<String, String>() {{put("k1", "v1");}}; + record.setMapField("map", map); + ZKUtil.createOrUpdate(_zkClient, path, record, true, true); + record = _zkClient.readData(path); + AssertJUnit.assertEquals(map, record.getMapField("map")); + + record = new ZNRecord("id7"); + Map<String, String> map2 = new HashMap<String, String>() {{put("k2", "v2");}}; + record.setMapField("map", map2); + ZKUtil.createOrUpdate(_zkClient, path, record, true, true); + record = _zkClient.readData(path); + AssertJUnit.assertEquals(new HashMap<String, String>() {{ + put("k2", "v2"); + }}, record.getMapField("map")); + } }
