diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/util/RadixNode.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/util/RadixNode.java
new file mode 100644
index 00000000000..3009c9a4e8c
--- /dev/null
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/util/RadixNode.java
@@ -0,0 +1,59 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.ozone.util;
+
+import java.util.HashMap;
+
+/**
+ * Wrapper class for Radix tree node representing Ozone prefix path segment
+ * separated by "/".
+ */
+public class RadixNode {
+
+ public RadixNode(String name) {
+ this.name = name;
+ this.children = new HashMap<>();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean hasChildren() {
+ return children.isEmpty();
+ }
+
+ public HashMap getChildren() {
+ return children;
+ }
+
+ public void setValue(T v) {
+ this.value = v;
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ private HashMap children;
+
+ private String name;
+
+ // TODO: k/v pairs for more metadata as needed
+ private T value;
+}
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/util/RadixTree.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/util/RadixTree.java
new file mode 100644
index 00000000000..72e9ab3f5e7
--- /dev/null
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/util/RadixTree.java
@@ -0,0 +1,214 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.ozone.util;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.hadoop.ozone.OzoneConsts;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+/**
+ * Wrapper class for handling Ozone prefix path lookup of ACL APIs
+ * with radix tree.
+ */
+public class RadixTree {
+
+ /**
+ * create a empty radix tree with root only.
+ */
+ public RadixTree() {
+ root = new RadixNode(PATH_DELIMITER);
+ }
+
+ /**
+ * If the Radix tree contains root only.
+ * @return true if the radix tree contains root only.
+ */
+ public boolean isEmpty() {
+ return root.hasChildren();
+ }
+
+ /**
+ * Insert prefix tree node without value, value can be ACL or other metadata
+ * of the prefix path.
+ * @param path
+ */
+ public void insert(String path) {
+ insert(path, null);
+ }
+
+ /**
+ * Insert prefix tree node with value, value can be ACL or other metadata
+ * of the prefix path.
+ * @param path
+ * @param val
+ */
+ public void insert(String path, T val) {
+ // all prefix path inserted should end with "/"
+ RadixNode n = root;
+ Path p = Paths.get(path);
+ for (int level = 0; level < p.getNameCount(); level++) {
+ HashMap child = n.getChildren();
+ String component = p.getName(level).toString();
+ if (child.containsKey(component)) {
+ n = child.get(component);
+ } else {
+ RadixNode tmp = new RadixNode(component);
+ child.put(component, tmp);
+ n = tmp;
+ }
+ }
+ if (val != null) {
+ n.setValue(val);
+ }
+ }
+
+ /**
+ * Get the last node in the exact prefix path that matches in the tree.
+ * @param path - prefix path
+ * @return last node in the prefix tree or null if non exact prefix matchl
+ */
+ public RadixNode getLastNodeInPrefixPath(String path) {
+ List> lpp = getLongestPrefixPath(path);
+ Path p = Paths.get(path);
+ if (lpp.size() != p.getNameCount() + 1) {
+ return null;
+ } else {
+ return lpp.get(p.getNameCount());
+ }
+ }
+
+ /**
+ * Remove prefix path.
+ * @param path
+ */
+ public void removePrefixPath(String path) {
+ Path p = Paths.get(path);
+ removePrefixPathInternal(root, p, 0);
+ }
+
+ /**
+ * Recursively remove non-overlapped part of the prefix path from radix tree.
+ * @param current current radix tree node.
+ * @param path prefix path to be removed.
+ * @param level current recursive level.
+ * @return true if current radix node can be removed.
+ * (not overlapped with other path),
+ * false otherwise.
+ */
+ private boolean removePrefixPathInternal(RadixNode current,
+ Path path, int level) {
+ // last component is processed
+ if (level == path.getNameCount()) {
+ return current.hasChildren();
+ }
+
+ // not last component, recur for next component
+ String name = path.getName(level).toString();
+ RadixNode node = current.getChildren().get(name);
+ if (node == null) {
+ return false;
+ }
+
+ if (removePrefixPathInternal(node, path, level+1)) {
+ current.getChildren().remove(name);
+ return current.hasChildren();
+ }
+ return false;
+ }
+
+ /**
+ * Get the longest prefix path.
+ * @param path - prefix path.
+ * @return longest prefix path as list of RadixNode.
+ */
+ public List> getLongestPrefixPath(String path) {
+ RadixNode n = root;
+ Path p = Paths.get(path);
+ int level = 0;
+ List> result = new ArrayList<>();
+ result.add(root);
+ while (level < p.getNameCount()) {
+ HashMap children = n.getChildren();
+ if (children.isEmpty()) {
+ break;
+ }
+ String component = p.getName(level).toString();
+ if (children.containsKey(component)) {
+ n = children.get(component);
+ result.add(n);
+ level++;
+ } else {
+ break;
+ }
+ }
+ return result;
+ }
+
+ @VisibleForTesting
+ /**
+ * Convert radix path to string format for output.
+ * @param path - radix path represented by list of radix nodes.
+ * @return radix path as string separated by "/".
+ * Note: the path will always be normalized with and ending "/".
+ */
+ public static String radixPathToString(List> path) {
+ StringBuilder sb = new StringBuilder();
+ for (RadixNode n : path) {
+ sb.append(n.getName());
+ sb.append(n.getName().equals(PATH_DELIMITER) ? "" : PATH_DELIMITER);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Get the longest prefix path.
+ * @param path - prefix path.
+ * @return longest prefix path as String separated by "/".
+ */
+ public String getLongestPrefix(String path) {
+ RadixNode n = root;
+ Path p = Paths.get(path);
+ int level = 0;
+ while (level < p.getNameCount()) {
+ HashMap children = n.getChildren();
+ if (children.isEmpty()) {
+ break;
+ }
+ String component = p.getName(level).toString();
+ if (children.containsKey(component)) {
+ n = children.get(component);
+ level++;
+ } else {
+ break;
+ }
+ }
+ return level >= 1 ?
+ Paths.get(root.getName()).resolve(p.subpath(0, level)).toString() :
+ root.getName();
+ }
+
+ // root of a radix tree has a name of "/" and may optionally has it value.
+ private RadixNode root;
+
+ private final static String PATH_DELIMITER = OzoneConsts.OZONE_URI_DELIMITER;
+}
diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/util/TestRadixTree.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/util/TestRadixTree.java
new file mode 100644
index 00000000000..ceed5346f81
--- /dev/null
+++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/util/TestRadixTree.java
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+package org.apache.hadoop.ozone.util;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test Ozone Radix tree operations.
+ */
+public class TestRadixTree {
+
+ final static RadixTree ROOT = new RadixTree<>();
+
+ @BeforeClass
+ public static void setupRadixTree() {
+ // Test prefix paths with an empty tree
+ assertEquals(true, ROOT.isEmpty());
+ assertEquals("/", ROOT.getLongestPrefix("/a/b/c"));
+ assertEquals("/", RadixTree.radixPathToString(
+ ROOT.getLongestPrefixPath("/a/g")));
+ // Build Radix tree below for testing.
+ // a
+ // |
+ // b
+ // / \
+ // c e
+ // / \ / \ \
+ // d f g dir1 dir2(1000)
+ // |
+ // g
+ // |
+ // h
+ ROOT.insert("/a/b/c/d");
+ ROOT.insert("/a/b/c/d/g/h");
+ ROOT.insert("/a/b/c/f");
+ ROOT.insert("/a/b/e/g");
+ ROOT.insert("/a/b/e/dir1");
+ ROOT.insert("/a/b/e/dir2", 1000);
+ }
+
+ /**
+ * Tests if insert and build prefix tree is correct.
+ */
+ @Test
+ public void testGetLongestPrefix() {
+ assertEquals("/a/b/c", ROOT.getLongestPrefix("/a/b/c"));
+ assertEquals("/a/b", ROOT.getLongestPrefix("/a/b"));
+ assertEquals("/a", ROOT.getLongestPrefix("/a"));
+ assertEquals("/a/b/e/g", ROOT.getLongestPrefix("/a/b/e/g/h"));
+
+ assertEquals("/", ROOT.getLongestPrefix("/d/b/c"));
+ assertEquals("/a/b/e", ROOT.getLongestPrefix("/a/b/e/dir3"));
+ assertEquals("/a/b/c/d", ROOT.getLongestPrefix("/a/b/c/d/p"));
+
+ assertEquals("/a/b/c/f", ROOT.getLongestPrefix("/a/b/c/f/p"));
+ }
+
+ @Test
+ public void testGetLongestPrefixPath() {
+ List> lpp =
+ ROOT.getLongestPrefixPath("/a/b/c/d/g/p");
+ RadixNode lpn = lpp.get(lpp.size()-1);
+ assertEquals("g", lpn.getName());
+ lpn.setValue(100);
+
+
+ List> lpq =
+ ROOT.getLongestPrefixPath("/a/b/c/d/g/q");
+ RadixNode lqn = lpp.get(lpq.size()-1);
+ System.out.print(RadixTree.radixPathToString(lpq));
+ assertEquals(lpn, lqn);
+ assertEquals("g", lqn.getName());
+ assertEquals(100, (int)lqn.getValue());
+
+
+ assertEquals("/a/", RadixTree.radixPathToString(
+ ROOT.getLongestPrefixPath("/a/g")));
+
+ }
+
+ @Test
+ public void testGetLastNoeInPrefixPath() {
+ assertEquals(null, ROOT.getLastNodeInPrefixPath("/a/g"));
+ RadixNode ln = ROOT.getLastNodeInPrefixPath("/a/b/e/dir1");
+ assertEquals("dir1", ln.getName());
+ }
+
+ @Test
+ public void testRemovePrefixPath() {
+
+ // Remove, test and restore
+ // Remove partially overlapped path
+ ROOT.removePrefixPath("/a/b/c/d/g/h");
+ assertEquals("/a/b/c", ROOT.getLongestPrefix("a/b/c/d"));
+ ROOT.insert("/a/b/c/d/g/h");
+
+ // Remove fully overlapped path
+ ROOT.removePrefixPath("/a/b/c/d");
+ assertEquals("/a/b/c/d", ROOT.getLongestPrefix("a/b/c/d"));
+ ROOT.insert("/a/b/c/d");
+
+ // Remove non existing path
+ ROOT.removePrefixPath("/d/a");
+ assertEquals("/a/b/c/d", ROOT.getLongestPrefix("a/b/c/d"));
+ }
+
+
+}
diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/util/package-info.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/util/package-info.java
new file mode 100644
index 00000000000..a6acd30d77c
--- /dev/null
+++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/util/package-info.java
@@ -0,0 +1,21 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.ozone.util;
+/**
+ * Unit tests of generic ozone utils.
+ */