You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by xy...@apache.org on 2023/04/27 20:36:11 UTC
[helix] 02/37: Add listener interfaces and config class (#2249)
This is an automated email from the ASF dual-hosted git repository.
xyuanlu pushed a commit to branch metaclient
in repository https://gitbox.apache.org/repos/asf/helix.git
commit 8629a81441fb54dc809755bdf94b283c57cf5764
Author: xyuanlu <xy...@gmail.com>
AuthorDate: Mon Nov 21 11:32:03 2022 -0800
Add listener interfaces and config class (#2249)
Add listener interfaces and factory class for metaclient interface
---
.../apache/helix/metaclient/api/AsyncCallback.java | 36 +-
.../helix/metaclient/api/ChildChangeListener.java | 39 +++
.../metaclient/api/ConnectStateChangeListener.java | 39 +++
.../helix/metaclient/api/DataChangeListener.java | 33 ++
.../metaclient/api/DirectChildChangeListener.java | 37 ++
.../metaclient/api/DirectChildSubscribeResult.java | 45 +++
.../helix/metaclient/api/MetaClientInterface.java | 371 ++++++++++++++++++---
.../metaclient/factories/MetaClientConfig.java | 115 +++++++
.../metaclient/factories/MetaClientFactory.java | 37 ++
.../helix/metaclient/impl/zk/ZkMetaClient.java | 245 ++++++++++++++
10 files changed, 954 insertions(+), 43 deletions(-)
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/AsyncCallback.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/AsyncCallback.java
index 1bfa5c6ab..88ae31623 100644
--- a/meta-client/src/main/java/org/apache/helix/metaclient/api/AsyncCallback.java
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/AsyncCallback.java
@@ -22,28 +22,58 @@ package org.apache.helix.metaclient.api;
import java.util.List;
/**
- * An asynchronous callback is deferred to invoke after an async CRUD operation returns.
- * The corresponding callback is registered when async CRUD API is invoked.
+ * An asynchronous callback is deferred to invoke after an async CRUD operation finish and return.
+ * The corresponding callback is registered when async CRUD API is invoked. Implementation processes
+ * the result of each CRUD call. It should check return code and perform accordingly.
*/
+// TODO: define return code. failure code should map to MetaClient exceptions.
public interface AsyncCallback {
//This callback is used when stat object is returned from the operation.
interface StatCallback extends AsyncCallback {
+ /**
+ * Process the result of asynchronous calls that returns a stat object.
+ * @param returnCode The return code of the call.
+ * @param key the key that passed to asynchronous calls.
+ * @param context context object that passed to asynchronous calls.
+ * @param stat the stats of the entry of the given key, returned from the async call.
+ */
void processResult(int returnCode, String key, Object context, MetaClientInterface.Stat stat);
}
//This callback is used when data is returned from the operation.
interface DataCallback extends AsyncCallback {
+ /**
+ * Process the result of asynchronous calls that returns entry data.
+ * @param returnCode The return code of the call.
+ * @param key The key that passed to asynchronous calls.
+ * @param context context object that passed to asynchronous calls.
+ * @param data returned entry data from the call.
+ * @param stat the stats of the entry of the given key.
+ */
void processResult(int returnCode, String key, Object context, byte[] data, MetaClientInterface.Stat stat);
}
//This callback is used when nothing is returned from the operation.
interface VoidCallback extends AsyncCallback {
+ /**
+ * Process the result of asynchronous calls that has no return value.
+ * @param returnCode The return code of the call.
+ * @param key he key that passed to asynchronous calls.
+ * @param context context object that passed to asynchronous calls.
+ */
void processResult(int returnCode, String key, Object context);
}
//This callback is used to process the list if OpResults from a single transactional call.
interface TransactionCallback extends AsyncCallback {
- void processResult(int returnCode, String key, Object context, List<OpResult> opResults);
+ /**
+ * Process the result of asynchronous transactional calls.
+ * @param returnCode The return code of the transaction call.
+ * @param keys List of keys passed to the async transactional call.
+ * @param context context object that passed to asynchronous calls.
+ * @param opResults The list of transactional results.
+ */
+ void processResult(int returnCode, List<String> keys, Object context, List<OpResult> opResults);
}
}
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/ChildChangeListener.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/ChildChangeListener.java
new file mode 100644
index 000000000..4d09450da
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/ChildChangeListener.java
@@ -0,0 +1,39 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Listener interface for children change events on a particular key. It includes new child
+ * creation, child deletion, child data change.
+ * TODO: add type for persist listener is removed
+ * For hierarchy key spaces like zookeeper, it refers to an entry's entire subtree.
+ * For flat key spaces, it refers to keys that matches `prefix*`.
+ */
+public interface ChildChangeListener {
+ enum ChangeType {
+ ENTRY_CREATED, // Any child entry created
+ ENTRY_DELETED, // Any child entry deleted
+ ENTRY_DATA_CHANGE // Any child entry has value change
+ }
+ /**
+ * Called when any child of the current key has changed.
+ */
+ void handleChildChange(String changedPath, ChangeType changeType) throws Exception;
+}
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/ConnectStateChangeListener.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/ConnectStateChangeListener.java
new file mode 100644
index 000000000..63e7ae4e8
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/ConnectStateChangeListener.java
@@ -0,0 +1,39 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+public interface ConnectStateChangeListener {
+ /**
+ * Called when the connection state has changed. I
+ * @param prevState previous state before state change event.
+ * @param currentState client state after state change event. If it is a one time listsner, it is
+ * possible that the metaclient state changes again
+ */
+ void handleConnectStateChanged(MetaClientInterface.ConnectState prevState, MetaClientInterface.ConnectState currentState) throws Exception;
+
+ /**
+ * Called when new connection failed to established.
+ * @param error error returned from metaclient or metadata service.
+ */
+ void handleConnectionEstablishmentError(final Throwable error) throws Exception;
+
+ // TODO: Consider add a callback for new connection when we add support for session ID.
+
+}
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/DataChangeListener.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/DataChangeListener.java
new file mode 100644
index 000000000..84d11b34f
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/DataChangeListener.java
@@ -0,0 +1,33 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Listener interface for events on a particular key, including entry creating, deleting and value change.
+ */
+public interface DataChangeListener {
+ enum ChangeType {
+ ENTRY_CREATED, // Entry created of the specific path that the listener register to
+ ENTRY_DELETED, // Entry deleted of the specific path that the listener register to
+ ENTRY_UPDATE // Entry value updated
+ }
+
+ void handleDataChange(String key, Object data, ChangeType changeType) throws Exception;
+}
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildChangeListener.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildChangeListener.java
new file mode 100644
index 000000000..83e67b8e6
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildChangeListener.java
@@ -0,0 +1,37 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Listener interface for direct child change event on a particular key. It includes new
+ * child creation or child deletion. The callback won't differentiate these types.
+ * For hierarchy key spaces like zookeeper, it refers to an entry's direct child nodes.
+ * For flat key spaces, it refers to keys that matches `prefix*separator`.
+ */
+public interface DirectChildChangeListener {
+ /**
+ * Called when there is a direct child entry creation or deleted.
+ * @param key The parent key where child change listener is subscribed. It would be the key
+ * passed to subscribeDirectChildChange.
+ * @throws Exception
+ */
+ void handleDirectChildChange(String key) throws Exception;
+
+}
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildSubscribeResult.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildSubscribeResult.java
new file mode 100644
index 000000000..6d45954b5
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildSubscribeResult.java
@@ -0,0 +1,45 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+
+
+public class DirectChildSubscribeResult {
+ // A list of direct children names at the time when change is subscribed.
+ // It includes only one level child name, does not include further sub children names.
+ private final List<String> _children;
+
+ // true means the listener is registered successfully.
+ private final boolean _isRegistered;
+
+ public DirectChildSubscribeResult(List<String> children, boolean isRegistered) {
+ _children = children;
+ _isRegistered = isRegistered;
+ }
+
+ public List<String> getDirectChildren() {
+ return _children;
+ }
+
+ public boolean isRegistered() {
+ return _isRegistered;
+ }
+}
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/MetaClientInterface.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/MetaClientInterface.java
index 4ad649c26..5118f6359 100644
--- a/meta-client/src/main/java/org/apache/helix/metaclient/api/MetaClientInterface.java
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/MetaClientInterface.java
@@ -20,51 +20,101 @@ package org.apache.helix.metaclient.api;
*/
import java.util.List;
+import java.util.concurrent.TimeUnit;
public interface MetaClientInterface<T> {
enum EntryMode {
- //The node will be removed automatically when the session associated with the creation
+ // The node will be removed automatically when the session associated with the creation
// of the node expires.
EPHEMERAL,
- //The node will not be automatically deleted upon client's disconnect.
- PERSISTENT
+
+ // The node will not be automatically deleted upon client's disconnect.
+ // An ephemeral node cannot have sub entry.
+ PERSISTENT,
+
+ // The node will not be automatically deleted when the last sub-entry of the node is deleted.
+ // The node is an ephemeral node.
+ CONTAINER
+ }
+
+ enum ConnectState {
+ // Client is connected to server
+ CONNECTED,
+
+ // Authentication failed.
+ AUTH_FAILED,
+
+ // Server has expired this connection.
+ EXPIRED,
+
+ // When client failed to connect server.
+ INIT_FAILED,
+
+ // When client explicitly call disconnect.
+ CLOSED_BY_CLIENT
}
/**
* Interface representing the metadata of an entry. It contains entry type and version number.
+ * TODO: we will add session ID to entry stats in the future
*/
class Stat {
private int _version;
private EntryMode _entryMode;
- public EntryMode getEntryType() {return _entryMode;}
- public int getVersion() {return _version;}
+ public EntryMode getEntryType() {
+ return _entryMode;
+ }
+
+ public int getVersion() {
+ return _version;
+ }
}
//synced CRUD API
- void create(final String key, T data, final EntryMode mode);
- void create(final String key, T data, final EntryMode mode, long ttl);
+ /**
+ * Create an persistent entry with given key and data. The entry will not be created if there is
+ * an existing entry with the same key.
+ * @param key key to identify the entry
+ * @param data value of the entry
+ */
+ void create(final String key, T data);
+
+ /**
+ * Create an entry of given EntryMode with given key and data. The entry will not be created if
+ * there is an existing entry with ethe same key.
+ * @param key key to identify the entry
+ * @param data value of the entry
+ * @param mode EntryMode identifying if the entry will be deleted upon client disconnect
+ */
+ void create(final String key, final T data, final EntryMode mode);
+
+ // TODO: add TTL create and renew API
/**
* Set the data for the entry of the given key if it exists and the given version matches the
* version of the node (if the given version is -1, it matches any node's versions).
+ * @param key key to identify the entry
+ * @param data new data of the entry
+ * @param version expected version of the entry. -1 matched any version.
*/
- void set(final String key, T data, int version);
+ void set(final String key, final T data, int version);
/**
* Update existing data of a given key using an updater. This method will issue a read to get
* current data and apply updater upon the current data.
- * @param updater : An updater that modifies the entry value.
+ * @param key key to identify the entry
+ * @param updater An updater that modifies the entry value.
* @return: the updated value.
*/
- T update(String key, DataUpdater<T> updater);
+ T update(final String key, DataUpdater<T> updater);
/**
* Check if there is an entry for the given key.
- * @param key
+ * @param key key to identify the entry
* @return return a Stat object if the entry exists. Return null otherwise.
*/
Stat exists(final String key);
@@ -72,87 +122,328 @@ public interface MetaClientInterface<T> {
/**
* Fetch the data for a given key.
* TODO: define exception type when key does not exist
+ * @param key key to identify the entry
+ * @return Return data of the entry
*/
- T get(String key);
+ T get(final String key);
/**
* API for transaction. The list of operation will be executed as an atomic operation.
* @param ops a list of operations. These operations will all be executed or non of them.
- * @return
+ * @return Return a list of OpResult.
*/
List<OpResult> transactionOP(final Iterable<Op> ops);
/**
- * Return a list of sub entries for the given keys
- * @param path: For metadata storage that has hierarchical key space (e.g. ZK), the path would be
- * a parent path,
- * For metadata storage that has non-hierarchical key space (e.g. etcd), the path would
- * be a prefix path.
+ * Return a list of children for the given keys.
+ * @param key For metadata storage that has hierarchical key space (e.g. ZK), the key would be
+ * a parent key,
+ * For metadata storage that has non-hierarchical key space (e.g. etcd), the key would
+ * be a prefix key.
+ * @eturn Return a list of children keys. Return direct child name only for hierarchical key
+ * space, return the whole sub key for non-hierarchical key space.
*/
- List<String> getSubEntryKeys(final String path);
+ List<String> getDirestChildrenKeys(final String key);
/**
- * Return the number of sub entries for the given keys
- * @param path: For metadata storage that has hierarchical key space (e.g. ZK), the path would be
- * a parent path,
- * For metadata storage that has non-hierarchical key space (e.g. etcd), the path would
- * be a prefix path.
+ * Return the number of children for the given keys.
+ * @param key For metadata storage that has hierarchical key space (e.g. ZK), the key would be
+ * a parent key,
+ * For metadata storage that has non-hierarchical key space (e.g. etcd), the key would
+ * be a prefix key.
*/
- int countSubEntries(final String path);
+ int countDirestChildren(final String key);
/**
* Remove the entry associated with the given key.
* For metadata storage that has hierarchical key space, the entry can only be deleted if the key
* has no child entry.
- * TODO: throws
- * @param path
- * @return
+ * TODO: define exception to throw
+ * @param key key to identify the entry to delete
+ * @return Return true if the deletion is completed
*/
- boolean delete(String path);
+ boolean delete(final String key);
/**
* Remove the entry associated with the given key.
* For metadata storage that has hierarchical key space, remove all its child entries as well
* For metadata storage that has non-hierarchical key space, this API is the same as delete()
- * @param path
- * @return
+ * @param key key to identify the entry to delete
+ * @return Return true if the deletion is completed
*/
- boolean recursiveDelete(String path);
+ boolean recursiveDelete(final String key);
/* Asynchronous methods return immediately.
* They take a callback object that will be executed either on successful execution of the request
* or on error with an appropriate return code indicating the error.
*/
- void asyncCreate(final String key, T data, int version, long ttl,
+
+ /**
+ * User may register callbacks for async CRUD calls. These callbacks will be executed in a async
+ * thread pool. User could define the thread pool size. Default value is 10.
+ * TODO: add const default value in a separate file
+ * @param poolSize pool size for executing user resisted async callbacks
+ */
+ void setAsyncExecPoolSize(int poolSize);
+
+ /**
+ * The asynchronous version of create.
+ * @param key key to identify the entry
+ * @param data value of the entry
+ * @param mode EntryMode identifying if the entry will be deleted upon client disconnect
+ * @param cb An user defined VoidCallback implementation that will be invoked when async create return.
+ * @see org.apache.helix.metaclient.api.AsyncCallback.VoidCallback
+ */
+ void asyncCreate(final String key, final T data, final EntryMode mode,
AsyncCallback.VoidCallback cb);
- void asyncSet(final String key, T data, int version, AsyncCallback.VoidCallback cb);
+ /**
+ * The asynchronous version of set.
+ * @param key key to identify the entry
+ * @param data new data of the entry
+ * @param version expected version if the entry. -1 matched any version
+ * @param cb An user defined VoidCallback implementation that will be invoked when async create return.
+ * @see org.apache.helix.metaclient.api.AsyncCallback.VoidCallback
+ */
+ void asyncSet(final String key, final T data, final int version, AsyncCallback.VoidCallback cb);
- void asyncUpdate(final String key, DataUpdater<T> updater, AsyncCallback.VoidCallback cb);
+ /**
+ * The asynchronous version of update.
+ * @param key key to identify the entry
+ * @param updater An updater that modifies the entry value.
+ * @param cb An user defined VoidCallback implementation that will be invoked when async create return.
+ * It will contain the newly updated data if update succeeded.
+ * @see org.apache.helix.metaclient.api.AsyncCallback.DataCallback
+ */
+ void asyncUpdate(final String key, DataUpdater<T> updater, AsyncCallback.DataCallback cb);
+ /**
+ * The asynchronous version of get.
+ * @param key key to identify the entry
+ * @param cb An user defined VoidCallback implementation that will be invoked when async get return.
+ * It will contain the entry data if get succeeded.
+ * @see org.apache.helix.metaclient.api.AsyncCallback.DataCallback
+ */
void asyncGet(final String key, AsyncCallback.DataCallback cb);
- void asyncCountSubEntries(final String path, AsyncCallback.DataCallback cb);
+ /**
+ * The asynchronous version of get sub entries.
+ * @param key key to identify the entry
+ * @param cb An user defined VoidCallback implementation that will be invoked when async count child return.
+ * It will contain the list of child keys if succeeded.
+ * @see org.apache.helix.metaclient.api.AsyncCallback.DataCallback
+ */
+ void asyncCountChildren(final String key, AsyncCallback.DataCallback cb);
+ /**
+ * The asynchronous version of get sub entries.
+ * @param key key to identify the entry
+ * @param cb An user defined VoidCallback implementation that will be invoked when async exist return.
+ * It will contain the stats of the entry if succeeded.
+ * @see org.apache.helix.metaclient.api.AsyncCallback.StatCallback
+ */
void asyncExist(final String key, AsyncCallback.StatCallback cb);
- void asyncDelete(final String keys, AsyncCallback.VoidCallback cb);
+ /**
+ * The asynchronous version of delete.
+ * @param key key to identify the entry
+ * @param cb An user defined VoidCallback implementation that will be invoked when async delete
+ * finish and return. @see org.apache.helix.metaclient.api.AsyncCallback.DataCallback
+ */
+ void asyncDelete(final String key, AsyncCallback.VoidCallback cb);
- void asyncTransaction(final String keys, AsyncCallback.TransactionCallback cb);
+ /**
+ * The asynchronous version of transaction operations.
+ * @param ops A list of operations
+ * @param cb An user defined TransactionCallback implementation that will be invoked when
+ * transaction operations finish and return. The TransactionCallback will contain
+ * either a list of OpResult if transaction finish successfully, or a return code
+ * indicating failure reason. @see org.apache.helix.metaclient.api.AsyncCallback.TransactionCallback
+ */
+ void asyncTransaction(final Iterable<Op> ops, AsyncCallback.TransactionCallback cb);
/* Batched APIs return result to user when all request finishes.
* These calls are not executed as a transaction.
*/
- boolean[] create(List<String> key, List<T> data, List<EntryMode> mode, List<Long> ttl);
- boolean[] set(List<String> keys, List<T> values, List<Integer> version);
+ /**
+ * Batch version of create. All entries will be created in persist mode. Returns when all request
+ * finishes. These calls are not executed as a transaction.
+ * @param key A list of key for create operations.
+ * @param data A list of data. Need to be in the same length of list of key.
+ * @return A list of boolean indicating create result of each operation.
+ */
+ boolean[] create(List<String> key, List<T> data);
+
+ /**
+ * Batch version of create. Returns when all request finishes. These calls are not executed as a
+ * transaction.
+ * @param key A list of key for create operations.
+ * @param data A list of data. Need to be in the same length of list of key.
+ * @param mode A list of EntryMode. Need to be in the same length of list of key.
+ * @return A list of boolean indicating create result of each operation.
+ */
+ boolean[] create(List<String> key, List<T> data, List<EntryMode> mode);
+
+ /**
+ * Batch version of set. Returns when all request finishes. These calls are not executed as a
+ * transaction.
+ * @param keys A list of key for set operations.
+ * @param datas A list of data. Need to be in the same length of list of key.
+ * @param version A list of expected version of the entry. -1 matched any version.
+ * Need to be in the same length of list of key.
+ * @return A list of boolean indicating set result of each operation.
+ */
+ boolean[] set(List<String> keys, List<T> datas, List<Integer> version);
+ /**
+ * Batch version of update. Returns when all request finishes. These calls are not executed as a
+ * transaction.
+ * @param keys A list of key for update operations.
+ * @param updater A list of updater. Need to be in the same length of list of key.
+ * @return A list of updated entry values.
+ */
List<T> update(List<String> keys, List<DataUpdater<T>> updater);
+ /**
+ * Batch version of get. Returns when all request finishes. These calls are not executed as a
+ * transaction.
+ * @param keys A list of key for get operations.
+ * @return A list of entry values.
+ */
List<T> get(List<String> keys);
+ /**
+ * Batch version of exists. Returns when all request finishes. These calls are not executed as a
+ * transaction.
+ * @param keys A list of key for exists operations.
+ * @return A list of stats for the given entries.
+ */
List<Stat> exists(List<String> keys);
+ /**
+ * Batch version of delete. Returns when all request finishes. These calls are not executed as a
+ * transaction.
+ * @param keys A list of key for delete operations.
+ * @return A list of boolean indicating delete result of each operation.
+ */
boolean[] delete(List<String> keys);
+ /**
+ * Maintains a connection with underlying metadata service based on config params. Connection
+ * created by this method will be used to perform CRUD operations on metadata service.
+ * @return True if connection is successfully established.
+ */
+ boolean connect();
+
+ /**
+ * Disconnect from server explicitly.
+ */
+ void disconnect();
+
+ /**
+ * @return client current connection state with metadata service.
+ */
+ ConnectState getClientConnectionState();
+
+ // Event notification APIs, user can register multiple listeners on the same key/connection state.
+ // All listeners will be automatically removed when client is disconnected.
+ // TODO: add auto re-register listener option
+
+ /**
+ * Subscribe change of a particular entry. Including entry data change, entry deletion and creation
+ * of the given key.
+ * @param key Key to identify the entry
+ * @param listener An implementation of DataChangeListener
+ * @see org.apache.helix.metaclient.api.DataChangeListener
+ * @param skipWatchingNonExistNode Will not register lister to an non-exist key if set to true.
+ * Please set to false if you are expecting ENTRY_CREATED type.
+ * @param persistListener The listener will persist when set to true. Otherwise it will be a one
+ * time triggered listener.
+ * @return Return an boolean indication if subscribe succeeded.
+ */
+ boolean subscribeDataChange(String key, DataChangeListener listener,
+ boolean skipWatchingNonExistNode, boolean persistListener);
+
+ /**
+ * Subscribe for direct child change event on a particular key. It includes new child
+ * creation or deletion. It does not include existing child data change.
+ * For hierarchy key spaces like zookeeper, it refers to an entry's direct children nodes.
+ * For flat key spaces, it refers to keys that matches `prefix*separator`.
+ * @param key key to identify the entry.
+ * @param listener An implementation of DirectSubEntryChangeListener.
+ * @see org.apache.helix.metaclient.api.DirectChildChangeListener
+ * @param skipWatchingNonExistNode If the passed in key does not exist, no listener wil be registered.
+ * @param persistListener The listener will persist when set to true. Otherwise it will be a one
+ * time triggered listener.
+ *
+ * @return Return an DirectSubEntrySubscribeResult. It will contain a list of direct sub children if
+ * subscribe succeeded.
+ */
+ DirectChildSubscribeResult subscribeDirectChildChange(String key,
+ DirectChildChangeListener listener, boolean skipWatchingNonExistNode,
+ boolean persistListener);
+
+ /**
+ * Subscribe for connection state change.
+ * @param listener An implementation of ConnectStateChangeListener.
+ * @see org.apache.helix.metaclient.api.ConnectStateChangeListener
+ * @param persistListener The listener will persist when set to true. Otherwise it will be a one
+ * time triggered listener.
+ *
+ * @return Return an boolean indication if subscribe succeeded.
+ */
+ boolean subscribeStateChanges(ConnectStateChangeListener listener, boolean persistListener);
+
+ /**
+ * Subscribe change for all children including entry change and data change.
+ * For hierarchy key spaces like zookeeper, it would watch the whole tree structure.
+ * For flat key spaces, it would watch for keys with certain prefix.
+ * @param key key to identify the entry.
+ * @param listener An implementation of ChildChangeListener.
+ * @see org.apache.helix.metaclient.api.ChildChangeListener
+ * @param skipWatchingNonExistNode If the passed in key does not exist, no listener wil be registered.
+ * @param persistListener The listener will persist when set to true. Otherwise it will be a one
+ * time triggered listener.
+ */
+ boolean subscribeChildChanges(String key, ChildChangeListener listener,
+ boolean skipWatchingNonExistNode, boolean persistListener);
+
+ /**
+ * Unsubscribe the listener to further changes. No-op if the listener is not subscribed to the key.
+ * @param key Key to identify the entry.
+ * @param listener The listener to unsubscribe.
+ */
+ void unsubscribeDataChange(String key, DataChangeListener listener);
+
+ /**
+ * Unsubscribe the listener to further changes. No-op if the listener is not subscribed to the key.
+ * @param key Key to identify the entry.
+ * @param listener The listener to unsubscribe.
+ */
+ void unsubscribeDirectChildChange(String key, DirectChildChangeListener listener);
+
+ /**
+ * Unsubscribe the listener to further changes. No-op if the listener is not subscribed to the key.
+ * @param key Key to identify the entry.
+ * @param listener The listener to unsubscribe.
+ */
+ void unsubscribeChildChanges(String key, ChildChangeListener listener);
+
+ /**
+ * Unsubscribe the listener to further changes. No-op if the listener is not subscribed to the key.
+ * @param listener The listener to unsubscribe.
+ */
+ void unsubscribeConnectStateChanges(ConnectStateChangeListener listener);
+
+ /**
+ * Block the call until the given key exists or timeout.
+ * @param key Key to monitor.
+ * @param timeUnit timeout unit
+ * @param timeOut timeout value
+ * @return
+ */
+ boolean waitUntilExists(String key, TimeUnit timeUnit, long timeOut);
+
+ // TODO: Secure CRUD APIs
}
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientConfig.java b/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientConfig.java
new file mode 100644
index 000000000..f317fd923
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientConfig.java
@@ -0,0 +1,115 @@
+package org.apache.helix.metaclient.factories;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+class MetaClientConfig {
+
+ public enum StoreType {
+ ZOOKEEPER, ETCD, CUSTOMIZED
+ }
+
+ private final String _connectionAddress;
+ private final long _connectionTimeout;
+ private final boolean _enableAuth;
+ private final StoreType _storeType;
+
+ public String getConnectionAddress() {
+ return _connectionAddress;
+ }
+
+ public long getConnectionTimeout() {
+ return _connectionTimeout;
+ }
+
+ public boolean isAuthEnabled() {
+ return _enableAuth;
+ }
+
+ public StoreType getStoreType() {
+ return _storeType;
+ }
+
+ // TODO: More options to add later
+ // private boolean _autoReRegistWatcher; // re-register one time watcher when set to true
+ // private boolean _resetWatchWhenReConnect; // re-register previous existing watcher when reconnect
+ //
+ // public enum RetryProtocol {
+ // NO_RETRY, EXP_BACK_OFF, CONST_RETRY_INTERVAL
+ // }
+ // private RetryProtocol _retryProtocol;
+
+
+ private MetaClientConfig(String connectionAddress, long connectionTimeout, boolean enableAuth,
+ StoreType storeType) {
+ _connectionAddress = connectionAddress;
+ _connectionTimeout = connectionTimeout;
+ _enableAuth = enableAuth;
+ _storeType = storeType;
+ }
+
+ public static class Builder {
+ private String _connectionAddress;
+
+ private long _connectionTimeout;
+ private boolean _enableAuth;
+ //private RetryProtocol _retryProtocol;
+ private StoreType _storeType;
+
+
+
+ public MetaClientConfig build() {
+ validate();
+ return new MetaClientConfig(_connectionAddress, _connectionTimeout, _enableAuth, _storeType);
+ }
+
+ public Builder() {
+ // set default values
+ setAuthEnabled(false);
+ setConnectionTimeout(-1);
+ }
+
+ public Builder setConnectionAddress(String connectionAddress) {
+ _connectionAddress = connectionAddress;
+ return this;
+ }
+
+ public Builder setAuthEnabled(Boolean enableAuth) {
+ _enableAuth = enableAuth;
+ return this;
+ }
+
+ public Builder setConnectionTimeout(long timeout) {
+ _connectionTimeout = timeout;
+ return this;
+ }
+
+ public Builder setStoreType(StoreType storeType) {
+ _storeType = storeType;
+ return this;
+ }
+
+ private void validate() {
+ if (_storeType == null || _connectionAddress == null) {
+ throw new IllegalArgumentException(
+ "MetaClientConfig.Builder: store type or connection string is null");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientFactory.java b/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientFactory.java
new file mode 100644
index 000000000..cf3feb0ef
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientFactory.java
@@ -0,0 +1,37 @@
+package org.apache.helix.metaclient.factories;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import org.apache.helix.metaclient.api.MetaClientInterface;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A factory class for MetaClient. It returns MetaClient entity based on config.
+ */
+public class MetaClientFactory {
+ private static final Logger LOG = LoggerFactory.getLogger(MetaClientFactory.class);
+
+ public MetaClientInterface getMetaClient(MetaClientConfig config) {
+ return null;
+ }
+}
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/ZkMetaClient.java b/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/ZkMetaClient.java
new file mode 100644
index 000000000..8437e387f
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/ZkMetaClient.java
@@ -0,0 +1,245 @@
+package org.apache.helix.metaclient.impl.zk;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.helix.metaclient.api.AsyncCallback;
+import org.apache.helix.metaclient.api.ChildChangeListener;
+import org.apache.helix.metaclient.api.ConnectStateChangeListener;
+import org.apache.helix.metaclient.api.DataChangeListener;
+import org.apache.helix.metaclient.api.DataUpdater;
+import org.apache.helix.metaclient.api.DirectChildChangeListener;
+import org.apache.helix.metaclient.api.DirectChildSubscribeResult;
+import org.apache.helix.metaclient.api.MetaClientInterface;
+import org.apache.helix.metaclient.api.OpResult;
+import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
+
+
+public class ZkMetaClient implements MetaClientInterface {
+
+ private RealmAwareZkClient _zkClient;
+
+ public ZkMetaClient() {
+
+ }
+
+ @Override
+ public void create(String key, Object data) {
+
+ }
+
+ @Override
+ public void create(String key, Object data, EntryMode mode) {
+
+ }
+
+ @Override
+ public void set(String key, Object data, int version) {
+
+ }
+
+ @Override
+ public Object update(String key, DataUpdater updater) {
+ return null;
+ }
+
+ @Override
+ public Stat exists(String key) {
+ return null;
+ }
+
+ @Override
+ public Object get(String key) {
+ return null;
+ }
+
+ @Override
+ public List<String> getDirestChildrenKeys(String key) {
+ return null;
+ }
+
+ @Override
+ public int countDirestChildren(String key) {
+ return 0;
+ }
+
+ @Override
+ public boolean delete(String key) {
+ return false;
+ }
+
+ @Override
+ public boolean recursiveDelete(String key) {
+ return false;
+ }
+
+ @Override
+ public void setAsyncExecPoolSize(int poolSize) {
+
+ }
+
+ @Override
+ public void asyncCreate(String key, Object data, EntryMode mode, AsyncCallback.VoidCallback cb) {
+
+ }
+
+ @Override
+ public void asyncSet(String key, Object data, int version, AsyncCallback.VoidCallback cb) {
+
+ }
+
+ @Override
+ public void asyncUpdate(String key, DataUpdater updater, AsyncCallback.DataCallback cb) {
+
+ }
+
+ @Override
+ public void asyncGet(String key, AsyncCallback.DataCallback cb) {
+
+ }
+
+ @Override
+ public void asyncCountChildren(String key, AsyncCallback.DataCallback cb) {
+
+ }
+
+ @Override
+ public void asyncExist(String key, AsyncCallback.StatCallback cb) {
+
+ }
+
+ @Override
+ public void asyncDelete(String keys, AsyncCallback.VoidCallback cb) {
+
+ }
+
+ @Override
+ public boolean[] create(List key, List data, List mode) {
+ return new boolean[0];
+ }
+
+ @Override
+ public boolean[] create(List key, List data) {
+ return new boolean[0];
+ }
+
+ @Override
+ public void asyncTransaction(Iterable iterable, AsyncCallback.TransactionCallback cb) {
+
+ }
+
+
+ @Override
+ public boolean connect() {
+ return false;
+ }
+
+ @Override
+ public void disconnect() {
+
+ }
+
+ @Override
+ public ConnectState getClientConnectionState() {
+ return null;
+ }
+
+ @Override
+ public boolean subscribeDataChange(String key, DataChangeListener listener,
+ boolean skipWatchingNonExistNode, boolean persistListener) {
+ return false;
+ }
+
+ @Override
+ public DirectChildSubscribeResult subscribeDirectChildChange(String key,
+ DirectChildChangeListener listener, boolean skipWatchingNonExistNode,
+ boolean persistListener) {
+ return null;
+ }
+
+ @Override
+ public boolean subscribeStateChanges(ConnectStateChangeListener listener,
+ boolean persistListener) {
+ return false;
+ }
+
+ @Override
+ public boolean subscribeChildChanges(String key, ChildChangeListener listener,
+ boolean skipWatchingNonExistNode, boolean persistListener) {
+ return false;
+ }
+
+ @Override
+ public void unsubscribeDataChange(String key, DataChangeListener listener) {
+
+ }
+
+ @Override
+ public void unsubscribeDirectChildChange(String key, DirectChildChangeListener listener) {
+
+ }
+
+ @Override
+ public void unsubscribeChildChanges(String key, ChildChangeListener listener) {
+
+ }
+
+ @Override
+ public void unsubscribeConnectStateChanges(ConnectStateChangeListener listener) {
+
+ }
+
+ @Override
+ public boolean waitUntilExists(String key, TimeUnit timeUnit, long time) {
+ return false;
+ }
+
+ @Override
+ public boolean[] delete(List keys) {
+ return new boolean[0];
+ }
+
+ @Override
+ public List<Stat> exists(List keys) {
+ return null;
+ }
+
+ @Override
+ public List get(List keys) {
+ return null;
+ }
+
+ @Override
+ public List update(List keys, List updater) {
+ return null;
+ }
+
+ @Override
+ public boolean[] set(List keys, List values, List version) {
+ return new boolean[0];
+ }
+
+ @Override
+ public List<OpResult> transactionOP(Iterable iterable) {
+ return null;
+ }
+}