You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by GitBox <gi...@apache.org> on 2018/04/27 01:31:36 UTC

[GitHub] merlimat closed pull request #1634: In pulsar admin Java API, unified persistent vs non-persistent into "topics()"

merlimat closed pull request #1634: In pulsar admin Java API, unified persistent vs non-persistent into "topics()"
URL: https://github.com/apache/incubator-pulsar/pull/1634
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java
index 10cff609a0..106e8c4e7c 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java
@@ -96,7 +96,7 @@
 import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
 import org.apache.pulsar.common.policies.data.PersistentOfflineTopicStats;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.Policies;
 import org.apache.pulsar.common.util.DateFormatter;
 import org.apache.pulsar.common.util.FutureUtil;
@@ -404,7 +404,7 @@ protected void internalDeletePartitionedTopic(boolean authoritative) {
             try {
                 for (int i = 0; i < numPartitions; i++) {
                     TopicName topicNamePartition = topicName.getPartition(i);
-                    pulsar().getAdminClient().persistentTopics().deleteAsync(topicNamePartition.toString())
+                    pulsar().getAdminClient().topics().deleteAsync(topicNamePartition.toString())
                             .whenComplete((r, ex) -> {
                                 if (ex != null) {
                                     if (ex instanceof NotFoundException) {
@@ -503,7 +503,7 @@ protected void internalDeleteTopic(boolean authoritative) {
             try {
                 // get the subscriptions only from the 1st partition since all the other partitions will have the same
                 // subscriptions
-                subscriptions.addAll(pulsar().getAdminClient().persistentTopics()
+                subscriptions.addAll(pulsar().getAdminClient().topics()
                         .getSubscriptions(topicName.getPartition(0).toString()));
             } catch (Exception e) {
                 throw new RestException(e);
@@ -523,7 +523,7 @@ protected void internalDeleteTopic(boolean authoritative) {
         return subscriptions;
     }
 
-    protected PersistentTopicStats internalGetStats(boolean authoritative) {
+    protected TopicStats internalGetStats(boolean authoritative) {
         validateAdminAndClientPermission();
         if (topicName.isGlobal()) {
             validateGlobalNamespaceOwnership(namespaceName);
@@ -575,7 +575,7 @@ protected PartitionedTopicStats internalGetPartitionedStats(boolean authoritativ
         PartitionedTopicStats stats = new PartitionedTopicStats(partitionMetadata);
         try {
             for (int i = 0; i < partitionMetadata.partitions; i++) {
-                PersistentTopicStats partitionStats = pulsar().getAdminClient().persistentTopics()
+                TopicStats partitionStats = pulsar().getAdminClient().topics()
                         .getStats(topicName.getPartition(i).toString());
                 stats.add(partitionStats);
                 stats.partitions.put(topicName.getPartition(i).toString(), partitionStats);
@@ -594,7 +594,7 @@ protected void internalDeleteSubscription(String subName, boolean authoritative)
         if (partitionMetadata.partitions > 0) {
             try {
                 for (int i = 0; i < partitionMetadata.partitions; i++) {
-                    pulsar().getAdminClient().persistentTopics()
+                    pulsar().getAdminClient().topics()
                             .deleteSubscription(topicName.getPartition(i).toString(), subName);
                 }
             } catch (Exception e) {
@@ -637,7 +637,7 @@ protected void internalSkipAllMessages(String subName, boolean authoritative) {
         if (partitionMetadata.partitions > 0) {
             try {
                 for (int i = 0; i < partitionMetadata.partitions; i++) {
-                    pulsar().getAdminClient().persistentTopics()
+                    pulsar().getAdminClient().topics()
                             .skipAllMessages(topicName.getPartition(i).toString(), subName);
                 }
             } catch (Exception e) {
@@ -707,7 +707,7 @@ protected void internalExpireMessagesForAllSubscriptions(int expireTimeInSeconds
             try {
                 // expire messages for each partition topic
                 for (int i = 0; i < partitionMetadata.partitions; i++) {
-                    pulsar().getAdminClient().persistentTopics().expireMessagesForAllSubscriptions(
+                    pulsar().getAdminClient().topics().expireMessagesForAllSubscriptions(
                             topicName.getPartition(i).toString(), expireTimeInSeconds);
                 }
             } catch (Exception e) {
@@ -740,7 +740,7 @@ protected void internalResetCursor(String subName, long timestamp, boolean autho
             Exception partitionException = null;
             try {
                 for (int i = 0; i < numParts; i++) {
-                    pulsar().getAdminClient().persistentTopics().resetCursor(topicName.getPartition(i).toString(),
+                    pulsar().getAdminClient().topics().resetCursor(topicName.getPartition(i).toString(),
                             subName, timestamp);
                 }
             } catch (PreconditionFailedException pfe) {
@@ -811,8 +811,7 @@ protected void internalCreateSubscription(String subscriptionName, MessageIdImpl
                 PulsarAdmin admin = pulsar().getAdminClient();
 
                 for (int i = 0; i < partitionMetadata.partitions; i++) {
-                    futures.add(admin.persistentTopics().createSubscriptionAsync(
-                            topicName.getPartition(i).toString(),
+                    futures.add(admin.topics().createSubscriptionAsync(topicName.getPartition(i).toString(),
                             subscriptionName, messageId));
                 }
 
@@ -1039,7 +1038,7 @@ protected void internalExpireMessages(String subName, int expireTimeInSeconds, b
             // expire messages for each partition topic
             try {
                 for (int i = 0; i < partitionMetadata.partitions; i++) {
-                    pulsar().getAdminClient().persistentTopics()
+                    pulsar().getAdminClient().topics()
                             .expireMessages(topicName.getPartition(i).toString(), subName, expireTimeInSeconds);
                 }
             } catch (Exception e) {
@@ -1237,13 +1236,13 @@ private PersistentReplicator getReplicatorReference(String replName, PersistentT
                 return;
             }
 
-            admin.persistentTopics().getStatsAsync(topicName.getPartition(0).toString()).thenAccept(stats -> {
+            admin.topics().getStatsAsync(topicName.getPartition(0).toString()).thenAccept(stats -> {
                 stats.subscriptions.keySet().forEach(subscription -> {
                     List<CompletableFuture<Void>> subscriptionFutures = new ArrayList<>();
                     for (int i = partitionMetadata.partitions; i < numPartitions; i++) {
                         final String topicNamePartition = topicName.getPartition(i).toString();
 
-                        subscriptionFutures.add(admin.persistentTopics().createSubscriptionAsync(topicNamePartition,
+                        subscriptionFutures.add(admin.topics().createSubscriptionAsync(topicNamePartition,
                                 subscription, MessageId.latest));
                     }
 
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/NonPersistentTopics.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/NonPersistentTopics.java
index fe4fb8a5eb..e9fbe0a0c0 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/NonPersistentTopics.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/NonPersistentTopics.java
@@ -41,6 +41,7 @@
 import javax.ws.rs.core.Response.Status;
 
 import org.apache.pulsar.broker.PulsarServerException;
+import org.apache.pulsar.broker.service.BrokerService;
 import org.apache.pulsar.broker.service.Topic;
 import org.apache.pulsar.broker.service.nonpersistent.NonPersistentTopic;
 import org.apache.pulsar.broker.web.RestException;
@@ -236,9 +237,11 @@ public void unloadTopic(@PathParam("property") String property, @PathParam("clus
         try {
             final List<String> topicList = Lists.newArrayList();
             pulsar().getBrokerService().getTopics().forEach((name, topicFuture) -> {
-                TopicName topicName = TopicName.get(name);
-                if (nsBundle.includes(topicName)) {
-                    topicList.add(name);
+                if (BrokerService.extractTopic(topicFuture).isPresent()) {
+                    TopicName topicName = TopicName.get(name);
+                    if (nsBundle.includes(topicName)) {
+                        topicList.add(name);
+                    }
                 }
             });
             return topicList;
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/PersistentTopics.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/PersistentTopics.java
index b03b533524..470ee45214 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/PersistentTopics.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/PersistentTopics.java
@@ -46,7 +46,7 @@
 import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
 import org.apache.pulsar.common.policies.data.PersistentOfflineTopicStats;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -228,7 +228,7 @@ public void deleteTopic(@PathParam("property") String property, @PathParam("clus
     @ApiOperation(hidden = true, value = "Get the stats for the topic.")
     @ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission"),
             @ApiResponse(code = 404, message = "Topic does not exist") })
-    public PersistentTopicStats getStats(@PathParam("property") String property, @PathParam("cluster") String cluster,
+    public TopicStats getStats(@PathParam("property") String property, @PathParam("cluster") String cluster,
             @PathParam("namespace") String namespace, @PathParam("topic") @Encoded String encodedTopic,
             @QueryParam("authoritative") @DefaultValue("false") boolean authoritative) {
         validateTopicName(property, cluster, namespace, encodedTopic);
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/NonPersistentTopics.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/NonPersistentTopics.java
index e4e3ae51cc..b1ee1710ac 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/NonPersistentTopics.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/NonPersistentTopics.java
@@ -41,6 +41,7 @@
 import javax.ws.rs.core.Response.Status;
 
 import org.apache.pulsar.broker.PulsarServerException;
+import org.apache.pulsar.broker.service.BrokerService;
 import org.apache.pulsar.broker.service.Topic;
 import org.apache.pulsar.broker.service.nonpersistent.NonPersistentTopic;
 import org.apache.pulsar.broker.web.RestException;
@@ -170,8 +171,7 @@ public void unloadTopic(@PathParam("tenant") String tenant, @PathParam("namespac
         for (int i = 0; i < boundaries.size() - 1; i++) {
             final String bundle = String.format("%s_%s", boundaries.get(i), boundaries.get(i + 1));
             try {
-                futures.add(pulsar().getAdminClient().nonPersistentTopics().getListInBundleAsync(namespaceName.toString(),
-                        bundle));
+                futures.add(pulsar().getAdminClient().topics().getListInBundleAsync(namespaceName.toString(), bundle));
             } catch (PulsarServerException e) {
                 log.error(String.format("[%s] Failed to get list of topics under namespace %s/%s", clientAppId(),
                         namespaceName, bundle), e);
@@ -228,9 +228,11 @@ public void unloadTopic(@PathParam("tenant") String tenant, @PathParam("namespac
         try {
             final List<String> topicList = Lists.newArrayList();
             pulsar().getBrokerService().getTopics().forEach((name, topicFuture) -> {
-                TopicName topicName = TopicName.get(name);
-                if (nsBundle.includes(topicName)) {
-                    topicList.add(name);
+                if (BrokerService.extractTopic(topicFuture).isPresent()) {
+                    TopicName topicName = TopicName.get(name);
+                    if (nsBundle.includes(topicName)) {
+                        topicList.add(name);
+                    }
                 }
             });
             return topicList;
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java
index 592a46bea9..5e1a98b1a7 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java
@@ -46,7 +46,7 @@
 import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
 import org.apache.pulsar.common.policies.data.PersistentOfflineTopicStats;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -223,7 +223,7 @@ public void deleteTopic(@PathParam("tenant") String tenant, @PathParam("namespac
     @ApiOperation(value = "Get the stats for the topic.")
     @ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission"),
             @ApiResponse(code = 404, message = "Topic does not exist") })
-    public PersistentTopicStats getStats(@PathParam("tenant") String tenant,
+    public TopicStats getStats(@PathParam("tenant") String tenant,
             @PathParam("namespace") String namespace, @PathParam("topic") @Encoded String encodedTopic,
             @QueryParam("authoritative") @DefaultValue("false") boolean authoritative) {
         validateTopicName(tenant, namespace, encodedTopic);
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java
index 683bb0331e..ad709db65a 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java
@@ -109,7 +109,7 @@
 import org.apache.pulsar.common.policies.data.ClusterData;
 import org.apache.pulsar.common.policies.data.PersistencePolicies;
 import org.apache.pulsar.common.policies.data.PersistentOfflineTopicStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.Policies;
 import org.apache.pulsar.common.policies.data.RetentionPolicies;
 import org.apache.pulsar.common.stats.Metrics;
@@ -1033,8 +1033,8 @@ public String generateUniqueProducerName() {
         return producerNameGenerator.getNextId();
     }
 
-    public Map<String, PersistentTopicStats> getTopicStats() {
-        HashMap<String, PersistentTopicStats> stats = new HashMap<>();
+    public Map<String, TopicStats> getTopicStats() {
+        HashMap<String, TopicStats> stats = new HashMap<>();
         topics.forEach((name, topicFuture) -> {
             Optional<Topic> topic = extractTopic(topicFuture);
             if (topic.isPresent()) {
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/Topic.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/Topic.java
index 9a6ab6e7fc..1f149f6c99 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/Topic.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/Topic.java
@@ -29,7 +29,7 @@
 import org.apache.pulsar.common.api.proto.PulsarApi.CommandSubscribe.SubType;
 import org.apache.pulsar.common.policies.data.BacklogQuota;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.Policies;
 import org.apache.pulsar.common.schema.SchemaData;
 import org.apache.pulsar.common.schema.SchemaVersion;
@@ -123,7 +123,7 @@ void updateRates(NamespaceStats nsStats, NamespaceBundleStats currentBundleStats
 
     ConcurrentOpenHashMap<String, ? extends Replicator> getReplicators();
 
-    PersistentTopicStats getStats();
+    TopicStats getStats();
 
     PersistentTopicInternalStats getInternalStats();
 
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentTopic.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentTopic.java
index 3e2783af07..66ff7ddf63 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentTopic.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/persistent/PersistentTopic.java
@@ -93,7 +93,7 @@
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats.CursorStats;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats.LedgerInfo;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.Policies;
 import org.apache.pulsar.common.policies.data.PublisherStats;
 import org.apache.pulsar.common.policies.data.ReplicatorStats;
@@ -175,14 +175,14 @@
     // Whether messages published must be encrypted or not in this topic
     private volatile boolean isEncryptionRequired = false;
 
-    private static final FastThreadLocal<TopicStats> threadLocalTopicStats = new FastThreadLocal<TopicStats>() {
+    private static final FastThreadLocal<TopicStatsHelper> threadLocalTopicStats = new FastThreadLocal<TopicStatsHelper>() {
         @Override
-        protected TopicStats initialValue() {
-            return new TopicStats();
+        protected TopicStatsHelper initialValue() {
+            return new TopicStatsHelper();
         }
     };
 
-    private static class TopicStats {
+    private static class TopicStatsHelper {
         public double averageMsgSize;
         public double aggMsgRateIn;
         public double aggMsgThroughputIn;
@@ -190,7 +190,7 @@ protected TopicStats initialValue() {
         public double aggMsgThroughputOut;
         public final ObjectObjectHashMap<String, PublisherStats> remotePublishersStats;
 
-        public TopicStats() {
+        public TopicStatsHelper() {
             remotePublishersStats = new ObjectObjectHashMap<String, PublisherStats>();
             reset();
         }
@@ -1097,8 +1097,8 @@ public ManagedLedger getManagedLedger() {
     public void updateRates(NamespaceStats nsStats, NamespaceBundleStats bundleStats, StatsOutputStream topicStatsStream,
             ClusterReplicationMetrics replStats, String namespace) {
 
-        TopicStats topicStats = threadLocalTopicStats.get();
-        topicStats.reset();
+        TopicStatsHelper topicStatsHelper = threadLocalTopicStats.get();
+        topicStatsHelper.reset();
 
         replicators.forEach((region, replicator) -> replicator.updateRates());
 
@@ -1110,11 +1110,11 @@ public void updateRates(NamespaceStats nsStats, NamespaceBundleStats bundleStats
             producer.updateRates();
             PublisherStats publisherStats = producer.getStats();
 
-            topicStats.aggMsgRateIn += publisherStats.msgRateIn;
-            topicStats.aggMsgThroughputIn += publisherStats.msgThroughputIn;
+            topicStatsHelper.aggMsgRateIn += publisherStats.msgRateIn;
+            topicStatsHelper.aggMsgThroughputIn += publisherStats.msgThroughputIn;
 
             if (producer.isRemote()) {
-                topicStats.remotePublishersStats.put(producer.getRemoteCluster(), publisherStats);
+                topicStatsHelper.remotePublishersStats.put(producer.getRemoteCluster(), publisherStats);
             }
         });
 
@@ -1124,7 +1124,7 @@ public void updateRates(NamespaceStats nsStats, NamespaceBundleStats bundleStats
 
         // Start replicator stats
         topicStatsStream.startObject("replication");
-        nsStats.replicatorCount += topicStats.remotePublishersStats.size();
+        nsStats.replicatorCount += topicStatsHelper.remotePublishersStats.size();
         replicators.forEach((cluster, replicator) -> {
             // Update replicator cursor state
             try {
@@ -1138,7 +1138,7 @@ public void updateRates(NamespaceStats nsStats, NamespaceBundleStats bundleStats
             ReplicatorStats rStat = replicator.getStats();
 
             // Add incoming msg rates
-            PublisherStats pubStats = topicStats.remotePublishersStats.get(replicator.getRemoteCluster());
+            PublisherStats pubStats = topicStatsHelper.remotePublishersStats.get(replicator.getRemoteCluster());
             if (pubStats != null) {
                 rStat.msgRateIn = pubStats.msgRateIn;
                 rStat.msgThroughputIn = pubStats.msgThroughputIn;
@@ -1146,8 +1146,8 @@ public void updateRates(NamespaceStats nsStats, NamespaceBundleStats bundleStats
                 rStat.inboundConnectedSince = pubStats.getConnectedSince();
             }
 
-            topicStats.aggMsgRateOut += rStat.msgRateOut;
-            topicStats.aggMsgThroughputOut += rStat.msgThroughputOut;
+            topicStatsHelper.aggMsgRateOut += rStat.msgRateOut;
+            topicStatsHelper.aggMsgThroughputOut += rStat.msgThroughputOut;
 
             // Populate replicator specific stats here
             topicStatsStream.startObject(cluster);
@@ -1259,8 +1259,8 @@ public void updateRates(NamespaceStats nsStats, NamespaceBundleStats bundleStats
                 // Close consumers
                 topicStatsStream.endObject();
 
-                topicStats.aggMsgRateOut += subMsgRateOut;
-                topicStats.aggMsgThroughputOut += subMsgThroughputOut;
+                topicStatsHelper.aggMsgRateOut += subMsgRateOut;
+                topicStatsHelper.aggMsgThroughputOut += subMsgThroughputOut;
                 nsStats.msgBacklog += subscription.getNumberOfEntriesInBacklog();
             } catch (Exception e) {
                 log.error("Got exception when creating consumer stats for subscription {}: {}", subscriptionName,
@@ -1272,36 +1272,36 @@ public void updateRates(NamespaceStats nsStats, NamespaceBundleStats bundleStats
         topicStatsStream.endObject();
 
         // Remaining dest stats.
-        topicStats.averageMsgSize = topicStats.aggMsgRateIn == 0.0 ? 0.0
-                : (topicStats.aggMsgThroughputIn / topicStats.aggMsgRateIn);
+        topicStatsHelper.averageMsgSize = topicStatsHelper.aggMsgRateIn == 0.0 ? 0.0
+                : (topicStatsHelper.aggMsgThroughputIn / topicStatsHelper.aggMsgRateIn);
         topicStatsStream.writePair("producerCount", producers.size());
-        topicStatsStream.writePair("averageMsgSize", topicStats.averageMsgSize);
-        topicStatsStream.writePair("msgRateIn", topicStats.aggMsgRateIn);
-        topicStatsStream.writePair("msgRateOut", topicStats.aggMsgRateOut);
-        topicStatsStream.writePair("msgThroughputIn", topicStats.aggMsgThroughputIn);
-        topicStatsStream.writePair("msgThroughputOut", topicStats.aggMsgThroughputOut);
+        topicStatsStream.writePair("averageMsgSize", topicStatsHelper.averageMsgSize);
+        topicStatsStream.writePair("msgRateIn", topicStatsHelper.aggMsgRateIn);
+        topicStatsStream.writePair("msgRateOut", topicStatsHelper.aggMsgRateOut);
+        topicStatsStream.writePair("msgThroughputIn", topicStatsHelper.aggMsgThroughputIn);
+        topicStatsStream.writePair("msgThroughputOut", topicStatsHelper.aggMsgThroughputOut);
         topicStatsStream.writePair("storageSize", ledger.getEstimatedBacklogSize());
         topicStatsStream.writePair("pendingAddEntriesCount", ((ManagedLedgerImpl) ledger).getPendingAddEntriesCount());
 
-        nsStats.msgRateIn += topicStats.aggMsgRateIn;
-        nsStats.msgRateOut += topicStats.aggMsgRateOut;
-        nsStats.msgThroughputIn += topicStats.aggMsgThroughputIn;
-        nsStats.msgThroughputOut += topicStats.aggMsgThroughputOut;
+        nsStats.msgRateIn += topicStatsHelper.aggMsgRateIn;
+        nsStats.msgRateOut += topicStatsHelper.aggMsgRateOut;
+        nsStats.msgThroughputIn += topicStatsHelper.aggMsgThroughputIn;
+        nsStats.msgThroughputOut += topicStatsHelper.aggMsgThroughputOut;
         nsStats.storageSize += ledger.getEstimatedBacklogSize();
 
-        bundleStats.msgRateIn += topicStats.aggMsgRateIn;
-        bundleStats.msgRateOut += topicStats.aggMsgRateOut;
-        bundleStats.msgThroughputIn += topicStats.aggMsgThroughputIn;
-        bundleStats.msgThroughputOut += topicStats.aggMsgThroughputOut;
+        bundleStats.msgRateIn += topicStatsHelper.aggMsgRateIn;
+        bundleStats.msgRateOut += topicStatsHelper.aggMsgRateOut;
+        bundleStats.msgThroughputIn += topicStatsHelper.aggMsgThroughputIn;
+        bundleStats.msgThroughputOut += topicStatsHelper.aggMsgThroughputOut;
         bundleStats.cacheSize += ((ManagedLedgerImpl) ledger).getCacheSize();
 
         // Close topic object
         topicStatsStream.endObject();
     }
 
-    public PersistentTopicStats getStats() {
+    public TopicStats getStats() {
 
-        PersistentTopicStats stats = new PersistentTopicStats();
+        TopicStats stats = new TopicStats();
 
         ObjectObjectHashMap<String, PublisherStats> remotePublishersStats = new ObjectObjectHashMap<String, PublisherStats>();
 
diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/metrics/AbstractMetrics.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/metrics/AbstractMetrics.java
index f9c73e79ea..94ce4920ee 100644
--- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/metrics/AbstractMetrics.java
+++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/stats/metrics/AbstractMetrics.java
@@ -29,7 +29,7 @@
 import org.apache.bookkeeper.mledger.impl.ManagedLedgerImpl;
 import org.apache.bookkeeper.mledger.impl.ManagedLedgerMBeanImpl;
 import org.apache.pulsar.broker.PulsarService;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.stats.Metrics;
 
 import com.google.common.collect.Lists;
@@ -237,8 +237,8 @@ protected void populateDimensionMap(Map<Metrics, List<ManagedLedgerImpl>> ledger
         }
     }
 
-    protected void populateDimensionMap(Map<Metrics, List<PersistentTopicStats>> topicsStatsByDimensionMap,
-            Metrics metrics, PersistentTopicStats destStats) {
+    protected void populateDimensionMap(Map<Metrics, List<TopicStats>> topicsStatsByDimensionMap,
+            Metrics metrics, TopicStats destStats) {
         if (!topicsStatsByDimensionMap.containsKey(metrics)) {
             // create new list
             topicsStatsByDimensionMap.put(metrics, Lists.newArrayList(destStats));
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest.java
index 675bd1ccc5..cd00d1c5c1 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest.java
@@ -70,7 +70,7 @@
 import org.apache.pulsar.client.admin.PulsarAdminException.NotFoundException;
 import org.apache.pulsar.client.admin.PulsarAdminException.PreconditionFailedException;
 import org.apache.pulsar.client.admin.internal.LookupImpl;
-import org.apache.pulsar.client.admin.internal.PersistentTopicsImpl;
+import org.apache.pulsar.client.admin.internal.TopicsImpl;
 import org.apache.pulsar.client.admin.internal.TenantsImpl;
 import org.apache.pulsar.client.api.Consumer;
 import org.apache.pulsar.client.api.ConsumerBuilder;
@@ -101,7 +101,7 @@
 import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
 import org.apache.pulsar.common.policies.data.PersistencePolicies;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.Policies;
 import org.apache.pulsar.common.policies.data.RetentionPolicies;
 import org.apache.pulsar.common.policies.data.TenantInfo;
@@ -647,7 +647,7 @@ public void namespaces() throws PulsarAdminException, PulsarServerException, Exc
             .messageRoutingMode(MessageRoutingMode.SinglePartition)
             .create();
         producer.close();
-        admin.persistentTopics().delete("persistent://prop-xyz/ns1/my-topic");
+        admin.topics().delete("persistent://prop-xyz/ns1/my-topic");
 
         admin.namespaces().unloadNamespaceBundle("prop-xyz/ns1", "0x00000000_0xffffffff");
         NamespaceName ns = NamespaceName.get("prop-xyz/ns1");
@@ -680,7 +680,7 @@ public void namespaces() throws PulsarAdminException, PulsarServerException, Exc
         // Force topic creation and namespace being loaded
         producer = pulsarClient.newProducer().topic("persistent://prop-xyz/use/ns2/my-topic").create();
         producer.close();
-        admin.persistentTopics().delete("persistent://prop-xyz/use/ns2/my-topic");
+        admin.topics().delete("persistent://prop-xyz/use/ns2/my-topic");
 
         // both unload and delete should succeed for ns2 on other broker with a redirect
         // otheradmin.namespaces().unload("prop-xyz/use/ns2");
@@ -688,12 +688,12 @@ public void namespaces() throws PulsarAdminException, PulsarServerException, Exc
 
     @Test(dataProvider = "topicName")
     public void persistentTopics(String topicName) throws Exception {
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/ns1"), Lists.newArrayList());
 
         final String persistentTopicName = "persistent://prop-xyz/ns1/" + topicName;
         // Force to create a topic
         publishMessagesOnPersistentTopic("persistent://prop-xyz/ns1/" + topicName, 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"),
+        assertEquals(admin.topics().getList("prop-xyz/ns1"),
                 Lists.newArrayList("persistent://prop-xyz/ns1/" + topicName));
 
         // create consumer and subscription
@@ -703,82 +703,82 @@ public void persistentTopics(String topicName) throws Exception {
         Consumer<byte[]> consumer = client.newConsumer().topic(persistentTopicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(persistentTopicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(persistentTopicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic("persistent://prop-xyz/ns1/" + topicName, 10);
 
-        PersistentTopicStats topicStats = admin.persistentTopics().getStats(persistentTopicName);
+        TopicStats topicStats = admin.topics().getStats(persistentTopicName);
         assertEquals(topicStats.subscriptions.keySet(), Sets.newTreeSet(Lists.newArrayList("my-sub")));
         assertEquals(topicStats.subscriptions.get("my-sub").consumers.size(), 1);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 10);
         assertEquals(topicStats.publishers.size(), 0);
 
-        PersistentTopicInternalStats internalStats = admin.persistentTopics().getInternalStats(persistentTopicName);
+        PersistentTopicInternalStats internalStats = admin.topics().getInternalStats(persistentTopicName);
         assertEquals(internalStats.cursors.keySet(), Sets.newTreeSet(Lists.newArrayList("my-sub")));
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(persistentTopicName, "my-sub", 3);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(persistentTopicName, "my-sub", 3);
         assertEquals(messages.size(), 3);
         for (int i = 0; i < 3; i++) {
             String expectedMessage = "message-" + i;
             assertEquals(messages.get(i).getData(), expectedMessage.getBytes());
         }
 
-        messages = admin.persistentTopics().peekMessages(persistentTopicName, "my-sub", 15);
+        messages = admin.topics().peekMessages(persistentTopicName, "my-sub", 15);
         assertEquals(messages.size(), 10);
         for (int i = 0; i < 10; i++) {
             String expectedMessage = "message-" + i;
             assertEquals(messages.get(i).getData(), expectedMessage.getBytes());
         }
 
-        admin.persistentTopics().skipMessages(persistentTopicName, "my-sub", 5);
-        topicStats = admin.persistentTopics().getStats(persistentTopicName);
+        admin.topics().skipMessages(persistentTopicName, "my-sub", 5);
+        topicStats = admin.topics().getStats(persistentTopicName);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 5);
 
-        admin.persistentTopics().skipAllMessages(persistentTopicName, "my-sub");
-        topicStats = admin.persistentTopics().getStats(persistentTopicName);
+        admin.topics().skipAllMessages(persistentTopicName, "my-sub");
+        topicStats = admin.topics().getStats(persistentTopicName);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 0);
 
         consumer.close();
         client.close();
 
-        admin.persistentTopics().deleteSubscription(persistentTopicName, "my-sub");
+        admin.topics().deleteSubscription(persistentTopicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(persistentTopicName), Lists.newArrayList());
-        topicStats = admin.persistentTopics().getStats(persistentTopicName);
+        assertEquals(admin.topics().getSubscriptions(persistentTopicName), Lists.newArrayList());
+        topicStats = admin.topics().getStats(persistentTopicName);
         assertEquals(topicStats.subscriptions.keySet(), Sets.newTreeSet());
         assertEquals(topicStats.publishers.size(), 0);
 
         try {
-            admin.persistentTopics().skipAllMessages(persistentTopicName, "my-sub");
+            admin.topics().skipAllMessages(persistentTopicName, "my-sub");
         } catch (NotFoundException e) {
         }
 
-        admin.persistentTopics().delete(persistentTopicName);
+        admin.topics().delete(persistentTopicName);
 
         try {
-            admin.persistentTopics().delete(persistentTopicName);
+            admin.topics().delete(persistentTopicName);
             fail("Should have received 404");
         } catch (NotFoundException e) {
         }
 
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/ns1"), Lists.newArrayList());
     }
 
     @Test(dataProvider = "topicName")
     public void partitionedTopics(String topicName) throws Exception {
-        assertEquals(admin.persistentTopics().getPartitionedTopicList("prop-xyz/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getPartitionedTopicList("prop-xyz/ns1"), Lists.newArrayList());
         final String partitionedTopicName = "persistent://prop-xyz/ns1/" + topicName;
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 4);
-        assertEquals(admin.persistentTopics().getPartitionedTopicList("prop-xyz/ns1"),
+        admin.topics().createPartitionedTopic(partitionedTopicName, 4);
+        assertEquals(admin.topics().getPartitionedTopicList("prop-xyz/ns1"),
                 Lists.newArrayList(partitionedTopicName));
 
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 4);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 4);
 
         // check if the virtual topic doesn't get created
-        List<String> topics = admin.persistentTopics().getList("prop-xyz/ns1");
+        List<String> topics = admin.topics().getList("prop-xyz/ns1");
         assertEquals(topics.size(), 0);
 
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata("persistent://prop-xyz/ns1/ds2").partitions,
+        assertEquals(admin.topics().getPartitionedTopicMetadata("persistent://prop-xyz/ns1/ds2").partitions,
                 0);
 
         // create consumer and subscription
@@ -788,10 +788,10 @@ public void partitionedTopics(String topicName) throws Exception {
         Consumer<byte[]> consumer = client.newConsumer().topic(partitionedTopicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(partitionedTopicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(partitionedTopicName), Lists.newArrayList("my-sub"));
 
         try {
-            admin.persistentTopics().deleteSubscription(partitionedTopicName, "my-sub");
+            admin.topics().deleteSubscription(partitionedTopicName, "my-sub");
             fail("should have failed");
         } catch (PulsarAdminException.PreconditionFailedException e) {
             // ok
@@ -802,12 +802,12 @@ public void partitionedTopics(String topicName) throws Exception {
         Consumer<byte[]> consumer1 = client.newConsumer().topic(partitionedTopicName).subscriptionName("my-sub-1")
                 .subscribe();
 
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getSubscriptions(partitionedTopicName)),
+        assertEquals(Sets.newHashSet(admin.topics().getSubscriptions(partitionedTopicName)),
                 Sets.newHashSet("my-sub", "my-sub-1"));
 
         consumer1.close();
-        admin.persistentTopics().deleteSubscription(partitionedTopicName, "my-sub-1");
-        assertEquals(admin.persistentTopics().getSubscriptions(partitionedTopicName), Lists.newArrayList("my-sub"));
+        admin.topics().deleteSubscription(partitionedTopicName, "my-sub-1");
+        assertEquals(admin.topics().getSubscriptions(partitionedTopicName), Lists.newArrayList("my-sub"));
 
         Producer<byte[]> producer = client.newProducer()
             .topic(partitionedTopicName)
@@ -820,12 +820,12 @@ public void partitionedTopics(String topicName) throws Exception {
             producer.send(message.getBytes());
         }
 
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getList("prop-xyz/ns1")),
+        assertEquals(Sets.newHashSet(admin.topics().getList("prop-xyz/ns1")),
                 Sets.newHashSet(partitionedTopicName + "-partition-0", partitionedTopicName + "-partition-1",
                         partitionedTopicName + "-partition-2", partitionedTopicName + "-partition-3"));
 
         // test cumulative stats for partitioned topic
-        PartitionedTopicStats topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, false);
+        PartitionedTopicStats topicStats = admin.topics().getPartitionedStats(partitionedTopicName, false);
         assertEquals(topicStats.subscriptions.keySet(), Sets.newTreeSet(Lists.newArrayList("my-sub")));
         assertEquals(topicStats.subscriptions.get("my-sub").consumers.size(), 1);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 10);
@@ -833,36 +833,36 @@ public void partitionedTopics(String topicName) throws Exception {
         assertEquals(topicStats.partitions, Maps.newHashMap());
 
         // test per partition stats for partitioned topic
-        topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, true);
+        topicStats = admin.topics().getPartitionedStats(partitionedTopicName, true);
         assertEquals(topicStats.metadata.partitions, 4);
         assertEquals(topicStats.partitions.keySet(),
                 Sets.newHashSet(partitionedTopicName + "-partition-0", partitionedTopicName + "-partition-1",
                         partitionedTopicName + "-partition-2", partitionedTopicName + "-partition-3"));
-        PersistentTopicStats partitionStats = topicStats.partitions.get(partitionedTopicName + "-partition-0");
+        TopicStats partitionStats = topicStats.partitions.get(partitionedTopicName + "-partition-0");
         assertEquals(partitionStats.publishers.size(), 1);
         assertEquals(partitionStats.subscriptions.get("my-sub").consumers.size(), 1);
         assertEquals(partitionStats.subscriptions.get("my-sub").msgBacklog, 3, 1);
 
         try {
-            admin.persistentTopics().skipMessages(partitionedTopicName, "my-sub", 5);
+            admin.topics().skipMessages(partitionedTopicName, "my-sub", 5);
             fail("skip messages for partitioned topics should fail");
         } catch (Exception e) {
             // ok
         }
 
-        admin.persistentTopics().skipAllMessages(partitionedTopicName, "my-sub");
-        topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, false);
+        admin.topics().skipAllMessages(partitionedTopicName, "my-sub");
+        topicStats = admin.topics().getPartitionedStats(partitionedTopicName, false);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 0);
 
         producer.close();
         consumer.close();
 
-        admin.persistentTopics().deleteSubscription(partitionedTopicName, "my-sub");
+        admin.topics().deleteSubscription(partitionedTopicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(partitionedTopicName), Lists.newArrayList());
+        assertEquals(admin.topics().getSubscriptions(partitionedTopicName), Lists.newArrayList());
 
         try {
-            admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 32);
+            admin.topics().createPartitionedTopic(partitionedTopicName, 32);
             fail("Should have failed as the partitioned topic already exists");
         } catch (ConflictException ce) {
         }
@@ -873,11 +873,11 @@ public void partitionedTopics(String topicName) throws Exception {
             .messageRoutingMode(MessageRoutingMode.SinglePartition)
             .create();
 
-        topics = admin.persistentTopics().getList("prop-xyz/ns1");
+        topics = admin.topics().getList("prop-xyz/ns1");
         assertEquals(topics.size(), 4);
 
         try {
-            admin.persistentTopics().deletePartitionedTopic(partitionedTopicName);
+            admin.topics().deletePartitionedTopic(partitionedTopicName);
             fail("The topic is busy");
         } catch (PreconditionFailedException pfe) {
             // ok
@@ -886,25 +886,25 @@ public void partitionedTopics(String topicName) throws Exception {
         producer.close();
         client.close();
 
-        admin.persistentTopics().deletePartitionedTopic(partitionedTopicName);
+        admin.topics().deletePartitionedTopic(partitionedTopicName);
 
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 0);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 0);
 
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 32);
+        admin.topics().createPartitionedTopic(partitionedTopicName, 32);
 
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 32);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 32);
 
         try {
-            admin.persistentTopics().deletePartitionedTopic("persistent://prop-xyz/ns1/ds2");
+            admin.topics().deletePartitionedTopic("persistent://prop-xyz/ns1/ds2");
             fail("Should have failed as the partitioned topic was not created");
         } catch (NotFoundException nfe) {
         }
 
-        admin.persistentTopics().deletePartitionedTopic(partitionedTopicName);
+        admin.topics().deletePartitionedTopic(partitionedTopicName);
 
         // delete a partitioned topic in a global namespace
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 4);
-        admin.persistentTopics().deletePartitionedTopic(partitionedTopicName);
+        admin.topics().createPartitionedTopic(partitionedTopicName, 4);
+        admin.topics().deletePartitionedTopic(partitionedTopicName);
     }
 
     @Test(dataProvider = "numBundles")
@@ -937,7 +937,7 @@ public void testNamespaceSplitBundle() throws Exception {
             .create();
         producer.send("message".getBytes());
         publishMessagesOnPersistentTopic(topicName, 0);
-        assertEquals(admin.persistentTopics().getList(namespace), Lists.newArrayList(topicName));
+        assertEquals(admin.topics().getList(namespace), Lists.newArrayList(topicName));
 
         try {
             admin.namespaces().splitNamespaceBundle(namespace, "0x00000000_0xffffffff", true);
@@ -967,7 +967,7 @@ public void testNamespaceSplitBundleConcurrent() throws Exception {
             .create();
         producer.send("message".getBytes());
         publishMessagesOnPersistentTopic(topicName, 0);
-        assertEquals(admin.persistentTopics().getList(namespace), Lists.newArrayList(topicName));
+        assertEquals(admin.topics().getList(namespace), Lists.newArrayList(topicName));
 
         try {
             admin.namespaces().splitNamespaceBundle(namespace, "0x00000000_0xffffffff", false);
@@ -1043,17 +1043,17 @@ public void testNamespaceSplitBundleConcurrent() throws Exception {
 
     @Test
     public void testNamespaceUnloadBundle() throws Exception {
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/ns1"), Lists.newArrayList());
 
         // Force to create a topic
         publishMessagesOnPersistentTopic("persistent://prop-xyz/ns1/ds2", 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"),
+        assertEquals(admin.topics().getList("prop-xyz/ns1"),
                 Lists.newArrayList("persistent://prop-xyz/ns1/ds2"));
 
         // create consumer and subscription
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic("persistent://prop-xyz/ns1/ds2")
                 .subscriptionName("my-sub").subscribe();
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/ns1/ds2"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/ns1/ds2"),
                 Lists.newArrayList("my-sub"));
 
         // Create producer
@@ -1088,7 +1088,7 @@ public void testNamespaceUnloadBundle() throws Exception {
         // Force reload of namespace and wait for topic to be ready
         for (int i = 0; i < 30; i++) {
             try {
-                admin.persistentTopics().getStats("persistent://prop-xyz/ns1/ds2");
+                admin.topics().getStats("persistent://prop-xyz/ns1/ds2");
                 break;
             } catch (PulsarAdminException e) {
                 LOG.warn("Failed to get topic stats.. {}", e.getMessage());
@@ -1096,8 +1096,8 @@ public void testNamespaceUnloadBundle() throws Exception {
             }
         }
 
-        admin.persistentTopics().deleteSubscription("persistent://prop-xyz/ns1/ds2", "my-sub");
-        admin.persistentTopics().delete("persistent://prop-xyz/ns1/ds2");
+        admin.topics().deleteSubscription("persistent://prop-xyz/ns1/ds2", "my-sub");
+        admin.topics().delete("persistent://prop-xyz/ns1/ds2");
     }
 
     @Test(dataProvider = "numBundles")
@@ -1105,17 +1105,17 @@ public void testNamespaceBundleUnload(Integer numBundles) throws Exception {
         admin.namespaces().createNamespace("prop-xyz/ns1-bundles", numBundles);
         admin.namespaces().setNamespaceReplicationClusters("prop-xyz/ns1-bundles", Sets.newHashSet("test"));
 
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1-bundles"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/ns1-bundles"), Lists.newArrayList());
 
         // Force to create a topic
         publishMessagesOnPersistentTopic("persistent://prop-xyz/ns1-bundles/ds2", 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1-bundles"),
+        assertEquals(admin.topics().getList("prop-xyz/ns1-bundles"),
                 Lists.newArrayList("persistent://prop-xyz/ns1-bundles/ds2"));
 
         // create consumer and subscription
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic("persistent://prop-xyz/ns1-bundles/ds2")
                 .subscriptionName("my-sub").subscribe();
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds2"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds2"),
                 Lists.newArrayList("my-sub"));
 
         // Create producer
@@ -1146,7 +1146,7 @@ public void testNamespaceBundleUnload(Integer numBundles) throws Exception {
         // Force reload of namespace and wait for topic to be ready
         for (int i = 0; i < 30; i++) {
             try {
-                admin.persistentTopics().getStats("persistent://prop-xyz/ns1-bundles/ds2");
+                admin.topics().getStats("persistent://prop-xyz/ns1-bundles/ds2");
                 break;
             } catch (PulsarAdminException e) {
                 LOG.warn("Failed to get topic stats.. {}", e.getMessage());
@@ -1154,8 +1154,8 @@ public void testNamespaceBundleUnload(Integer numBundles) throws Exception {
             }
         }
 
-        admin.persistentTopics().deleteSubscription("persistent://prop-xyz/ns1-bundles/ds2", "my-sub");
-        admin.persistentTopics().delete("persistent://prop-xyz/ns1-bundles/ds2");
+        admin.topics().deleteSubscription("persistent://prop-xyz/ns1-bundles/ds2", "my-sub");
+        admin.topics().delete("persistent://prop-xyz/ns1-bundles/ds2");
     }
 
     @Test(dataProvider = "bundling")
@@ -1203,25 +1203,25 @@ public void testClearBacklogOnNamespace(Integer numBundles) throws Exception {
 
         admin.namespaces().clearNamespaceBacklogForSubscription("prop-xyz/ns1-bundles", "my-sub");
 
-        long backlog = admin.persistentTopics().getStats("persistent://prop-xyz/ns1-bundles/ds2").subscriptions
+        long backlog = admin.topics().getStats("persistent://prop-xyz/ns1-bundles/ds2").subscriptions
                 .get("my-sub").msgBacklog;
         assertEquals(backlog, 0);
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/ns1-bundles/ds1").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/ns1-bundles/ds1").subscriptions
                 .get("my-sub").msgBacklog;
         assertEquals(backlog, 0);
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/ns1-bundles/ds1").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/ns1-bundles/ds1").subscriptions
                 .get("my-sub-1").msgBacklog;
         assertEquals(backlog, 10);
 
         admin.namespaces().clearNamespaceBacklog("prop-xyz/ns1-bundles");
 
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/ns1-bundles/ds1").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/ns1-bundles/ds1").subscriptions
                 .get("my-sub-1").msgBacklog;
         assertEquals(backlog, 0);
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/ns1-bundles/ds2").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/ns1-bundles/ds2").subscriptions
                 .get("my-sub-1").msgBacklog;
         assertEquals(backlog, 0);
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/ns1-bundles/ds2").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/ns1-bundles/ds2").subscriptions
                 .get("my-sub-2").msgBacklog;
         assertEquals(backlog, 0);
     }
@@ -1263,9 +1263,9 @@ public void testUnsubscribeOnNamespace(Integer numBundles) throws Exception {
 
         admin.namespaces().unsubscribeNamespace("prop-xyz/ns1-bundles", "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds2"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds2"),
                 Lists.newArrayList("my-sub-1", "my-sub-2"));
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds1"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds1"),
                 Lists.newArrayList("my-sub-1"));
 
         consumer2.close();
@@ -1273,9 +1273,9 @@ public void testUnsubscribeOnNamespace(Integer numBundles) throws Exception {
 
         admin.namespaces().unsubscribeNamespace("prop-xyz/ns1-bundles", "my-sub-1");
 
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds2"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds2"),
                 Lists.newArrayList("my-sub-2"));
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds1"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/ns1-bundles/ds1"),
                 Lists.newArrayList());
     }
 
@@ -1326,7 +1326,7 @@ public void backlogQuotas() throws Exception {
     @Test
     public void statsOnNonExistingTopics() throws Exception {
         try {
-            admin.persistentTopics().getStats("persistent://prop-xyz/ns1/ghostTopic");
+            admin.topics().getStats("persistent://prop-xyz/ns1/ghostTopic");
             fail("The topic doesn't exist");
         } catch (NotFoundException e) {
             // OK
@@ -1343,7 +1343,7 @@ public void testDeleteFailedReturnCode() throws Exception {
             .create();
 
         try {
-            admin.persistentTopics().delete(topicName);
+            admin.topics().delete(topicName);
             fail("The topic is busy");
         } catch (PreconditionFailedException e) {
             // OK
@@ -1354,14 +1354,14 @@ public void testDeleteFailedReturnCode() throws Exception {
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("sub").subscribe();
 
         try {
-            admin.persistentTopics().delete(topicName);
+            admin.topics().delete(topicName);
             fail("The topic is busy");
         } catch (PreconditionFailedException e) {
             // OK
         }
 
         try {
-            admin.persistentTopics().deleteSubscription(topicName, "sub");
+            admin.topics().deleteSubscription(topicName, "sub");
             fail("The topic is busy");
         } catch (PreconditionFailedException e) {
             // Ok
@@ -1370,7 +1370,7 @@ public void testDeleteFailedReturnCode() throws Exception {
         consumer.close();
 
         // Now should succeed
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
     }
 
     private static class IncompatibleTenantAdmin {
@@ -1415,7 +1415,7 @@ public void testBackwardCompatiblity() throws Exception {
     public void persistentTopicsCursorReset(String topicName) throws Exception {
         admin.namespaces().setRetention("prop-xyz/ns1", new RetentionPolicies(10, 10));
 
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/ns1"), Lists.newArrayList());
 
         topicName = "persistent://prop-xyz/ns1/" + topicName;
 
@@ -1423,7 +1423,7 @@ public void persistentTopicsCursorReset(String topicName) throws Exception {
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).acknowledgmentGroupTime(0, TimeUnit.SECONDS).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, 5, 0);
 
@@ -1433,7 +1433,7 @@ public void persistentTopicsCursorReset(String topicName) throws Exception {
 
         publishMessagesOnPersistentTopic(topicName, 5, 5);
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(topicName, "my-sub", 10);
         assertEquals(messages.size(), 10);
 
         for (int i = 0; i < 10; i++) {
@@ -1442,7 +1442,7 @@ public void persistentTopicsCursorReset(String topicName) throws Exception {
         }
         // messages should still be available due to retention
 
-        admin.persistentTopics().resetCursor(topicName, "my-sub", messageTimestamp);
+        admin.topics().resetCursor(topicName, "my-sub", messageTimestamp);
 
         int receivedAfterReset = 0;
 
@@ -1457,16 +1457,16 @@ public void persistentTopicsCursorReset(String topicName) throws Exception {
 
         consumer.close();
 
-        admin.persistentTopics().deleteSubscription(topicName, "my-sub");
+        admin.topics().deleteSubscription(topicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList());
-        admin.persistentTopics().delete(topicName);
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList());
+        admin.topics().delete(topicName);
     }
 
     @Test(dataProvider = "topicName")
     public void persistentTopicsCursorResetAfterReset(String topicName) throws Exception {
         admin.namespaces().setRetention("prop-xyz/ns1", new RetentionPolicies(10, 10));
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/ns1"), Lists.newArrayList());
 
         topicName = "persistent://prop-xyz/ns1/" + topicName;
 
@@ -1474,7 +1474,7 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).acknowledgmentGroupTime(0, TimeUnit.SECONDS).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, 5, 0);
 
@@ -1488,7 +1488,7 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
 
         publishMessagesOnPersistentTopic(topicName, 2, 8);
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(topicName, "my-sub", 10);
         assertEquals(messages.size(), 10);
         messages.forEach(message -> {
             LOG.info("Peeked message: {}", new String(message.getData()));
@@ -1499,7 +1499,7 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
             consumer.acknowledge(message);
         }
 
-        admin.persistentTopics().resetCursor(topicName, "my-sub", firstTimestamp);
+        admin.topics().resetCursor(topicName, "my-sub", firstTimestamp);
 
         int receivedAfterReset = 0;
 
@@ -1515,7 +1515,7 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
 
         // Reset at 2nd timestamp
         receivedAfterReset = 0;
-        admin.persistentTopics().resetCursor(topicName, "my-sub", secondTimestamp);
+        admin.topics().resetCursor(topicName, "my-sub", secondTimestamp);
 
         // Should received messages from 7-9
         for (int i = 7; i < 10; i++) {
@@ -1528,10 +1528,10 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
         assertEquals(receivedAfterReset, 3);
 
         consumer.close();
-        admin.persistentTopics().deleteSubscription(topicName, "my-sub");
+        admin.topics().deleteSubscription(topicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList());
-        admin.persistentTopics().delete(topicName);
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList());
+        admin.topics().delete(topicName);
     }
 
     @Test(dataProvider = "topicName")
@@ -1539,16 +1539,16 @@ public void partitionedTopicsCursorReset(String topicName) throws Exception {
         admin.namespaces().setRetention("prop-xyz/ns1", new RetentionPolicies(10, 10));
         topicName = "persistent://prop-xyz/ns1/" + topicName;
 
-        admin.persistentTopics().createPartitionedTopic(topicName, 4);
+        admin.topics().createPartitionedTopic(topicName, 4);
 
         // create consumer and subscription
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).acknowledgmentGroupTime(0, TimeUnit.SECONDS).subscribe();
 
-        List<String> topics = admin.persistentTopics().getList("prop-xyz/ns1");
+        List<String> topics = admin.topics().getList("prop-xyz/ns1");
         assertEquals(topics.size(), 4);
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, 5, 0);
         Thread.sleep(1);
@@ -1562,7 +1562,7 @@ public void partitionedTopicsCursorReset(String topicName) throws Exception {
         }
         // messages should still be available due to retention
 
-        admin.persistentTopics().resetCursor(topicName, "my-sub", timestamp);
+        admin.topics().resetCursor(topicName, "my-sub", timestamp);
 
         Set<String> expectedMessages = Sets.newHashSet();
         Set<String> receivedMessages = Sets.newHashSet();
@@ -1577,20 +1577,20 @@ public void partitionedTopicsCursorReset(String topicName) throws Exception {
         assertEquals(receivedMessages.size(), 0);
 
         consumer.close();
-        admin.persistentTopics().deleteSubscription(topicName, "my-sub");
-        admin.persistentTopics().deletePartitionedTopic(topicName);
+        admin.topics().deleteSubscription(topicName, "my-sub");
+        admin.topics().deletePartitionedTopic(topicName);
     }
 
     @Test
     public void persistentTopicsInvalidCursorReset() throws Exception {
         admin.namespaces().setRetention("prop-xyz/ns1", new RetentionPolicies(10, 10));
 
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/ns1"), Lists.newArrayList());
 
         String topicName = "persistent://prop-xyz/ns1/invalidcursorreset";
         // Force to create a topic
         publishMessagesOnPersistentTopic(topicName, 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"), Lists.newArrayList(topicName));
+        assertEquals(admin.topics().getList("prop-xyz/ns1"), Lists.newArrayList(topicName));
 
         // create consumer and subscription
         URL pulsarUrl = new URL("http://127.0.0.1" + ":" + BROKER_WEBSERVICE_PORT);
@@ -1599,11 +1599,11 @@ public void persistentTopicsInvalidCursorReset() throws Exception {
         Consumer<byte[]> consumer = client.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, 10);
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(topicName, "my-sub", 10);
         assertEquals(messages.size(), 10);
 
         for (int i = 0; i < 10; i++) {
@@ -1612,21 +1612,21 @@ public void persistentTopicsInvalidCursorReset() throws Exception {
         }
         // use invalid timestamp
         try {
-            admin.persistentTopics().resetCursor(topicName, "my-sub", System.currentTimeMillis() - 190000);
+            admin.topics().resetCursor(topicName, "my-sub", System.currentTimeMillis() - 190000);
         } catch (Exception e) {
             // fail the test
             throw e;
         }
 
-        admin.persistentTopics().resetCursor(topicName, "my-sub", System.currentTimeMillis() + 90000);
+        admin.topics().resetCursor(topicName, "my-sub", System.currentTimeMillis() + 90000);
         consumer = client.newConsumer().topic(topicName).subscriptionName("my-sub").subscribe();
         consumer.close();
         client.close();
 
-        admin.persistentTopics().deleteSubscription(topicName, "my-sub");
+        admin.topics().deleteSubscription(topicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList());
-        admin.persistentTopics().delete(topicName);
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList());
+        admin.topics().delete(topicName);
     }
 
     @Test
@@ -1666,7 +1666,7 @@ public void testPersistentTopicsExpireMessages() throws Exception {
 
         // Force to create a topic
         publishMessagesOnPersistentTopic("persistent://prop-xyz/ns1/ds2", 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/ns1"),
+        assertEquals(admin.topics().getList("prop-xyz/ns1"),
                 Lists.newArrayList("persistent://prop-xyz/ns1/ds2"));
 
         // create consumer and subscription
@@ -1679,28 +1679,28 @@ public void testPersistentTopicsExpireMessages() throws Exception {
         Consumer<byte[]> consumer2 = consumerBuilder.clone().subscriptionName("my-sub2").subscribe();
         Consumer<byte[]> consumer3 = consumerBuilder.clone().subscriptionName("my-sub3").subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/ns1/ds2").size(), 3);
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/ns1/ds2").size(), 3);
 
         publishMessagesOnPersistentTopic("persistent://prop-xyz/ns1/ds2", 10);
 
-        PersistentTopicStats topicStats = admin.persistentTopics().getStats("persistent://prop-xyz/ns1/ds2");
+        TopicStats topicStats = admin.topics().getStats("persistent://prop-xyz/ns1/ds2");
         assertEquals(topicStats.subscriptions.get("my-sub1").msgBacklog, 10);
         assertEquals(topicStats.subscriptions.get("my-sub2").msgBacklog, 10);
         assertEquals(topicStats.subscriptions.get("my-sub3").msgBacklog, 10);
 
         Thread.sleep(1000); // wait for 1 seconds to expire message
-        admin.persistentTopics().expireMessages("persistent://prop-xyz/ns1/ds2", "my-sub1", 1);
+        admin.topics().expireMessages("persistent://prop-xyz/ns1/ds2", "my-sub1", 1);
         Thread.sleep(1000); // wait for 1 seconds to execute expire message as it is async
 
-        topicStats = admin.persistentTopics().getStats("persistent://prop-xyz/ns1/ds2");
+        topicStats = admin.topics().getStats("persistent://prop-xyz/ns1/ds2");
         assertEquals(topicStats.subscriptions.get("my-sub1").msgBacklog, 0);
         assertEquals(topicStats.subscriptions.get("my-sub2").msgBacklog, 10);
         assertEquals(topicStats.subscriptions.get("my-sub3").msgBacklog, 10);
 
-        admin.persistentTopics().expireMessagesForAllSubscriptions("persistent://prop-xyz/ns1/ds2", 1);
+        admin.topics().expireMessagesForAllSubscriptions("persistent://prop-xyz/ns1/ds2", 1);
         Thread.sleep(1000); // wait for 1 seconds to execute expire message as it is async
 
-        topicStats = admin.persistentTopics().getStats("persistent://prop-xyz/ns1/ds2");
+        topicStats = admin.topics().getStats("persistent://prop-xyz/ns1/ds2");
         assertEquals(topicStats.subscriptions.get("my-sub1").msgBacklog, 0);
         assertEquals(topicStats.subscriptions.get("my-sub2").msgBacklog, 0);
         assertEquals(topicStats.subscriptions.get("my-sub3").msgBacklog, 0);
@@ -1719,7 +1719,7 @@ public void testPersistentTopicsExpireMessages() throws Exception {
     @Test
     public void testPersistentTopicExpireMessageOnParitionTopic() throws Exception {
 
-        admin.persistentTopics().createPartitionedTopic("persistent://prop-xyz/ns1/ds1", 4);
+        admin.topics().createPartitionedTopic("persistent://prop-xyz/ns1/ds1", 4);
 
         // create consumer and subscription
         URL pulsarUrl = new URL("http://127.0.0.1" + ":" + BROKER_WEBSERVICE_PORT);
@@ -1738,22 +1738,22 @@ public void testPersistentTopicExpireMessageOnParitionTopic() throws Exception {
             producer.send(message.getBytes());
         }
 
-        PartitionedTopicStats topicStats = admin.persistentTopics().getPartitionedStats("persistent://prop-xyz/ns1/ds1",
+        PartitionedTopicStats topicStats = admin.topics().getPartitionedStats("persistent://prop-xyz/ns1/ds1",
                 true);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 10);
 
-        PersistentTopicStats partitionStatsPartition0 = topicStats.partitions
+        TopicStats partitionStatsPartition0 = topicStats.partitions
                 .get("persistent://prop-xyz/ns1/ds1-partition-0");
-        PersistentTopicStats partitionStatsPartition1 = topicStats.partitions
+        TopicStats partitionStatsPartition1 = topicStats.partitions
                 .get("persistent://prop-xyz/ns1/ds1-partition-1");
         assertEquals(partitionStatsPartition0.subscriptions.get("my-sub").msgBacklog, 3, 1);
         assertEquals(partitionStatsPartition1.subscriptions.get("my-sub").msgBacklog, 3, 1);
 
         Thread.sleep(1000);
-        admin.persistentTopics().expireMessagesForAllSubscriptions("persistent://prop-xyz/ns1/ds1", 1);
+        admin.topics().expireMessagesForAllSubscriptions("persistent://prop-xyz/ns1/ds1", 1);
         Thread.sleep(1000);
 
-        topicStats = admin.persistentTopics().getPartitionedStats("persistent://prop-xyz/ns1/ds1", true);
+        topicStats = admin.topics().getPartitionedStats("persistent://prop-xyz/ns1/ds1", true);
         partitionStatsPartition0 = topicStats.partitions.get("persistent://prop-xyz/ns1/ds1-partition-0");
         partitionStatsPartition1 = topicStats.partitions.get("persistent://prop-xyz/ns1/ds1-partition-1");
         assertEquals(partitionStatsPartition0.subscriptions.get("my-sub").msgBacklog, 0);
@@ -1780,19 +1780,20 @@ public void testPulsarAdminForUriAndUrlEncoding(String topicName) throws Excepti
         final String urlEncodedTopic = Codec.encode(topicName);
         final String uriEncodedTopic = urlEncodedTopic.replaceAll("\\+", "%20");
         final int numOfPartitions = 4;
-        admin.persistentTopics().createPartitionedTopic(topic1, numOfPartitions);
+        admin.topics().createPartitionedTopic(topic1, numOfPartitions);
         // Create a consumer to get stats on this topic
         pulsarClient.newConsumer().topic(topic1).subscriptionName("my-subscriber-name").subscribe();
 
-        PersistentTopicsImpl persistent = (PersistentTopicsImpl) admin.persistentTopics();
-        Field field = PersistentTopicsImpl.class.getDeclaredField("adminV2PersistentTopics");
+        TopicsImpl persistent = (TopicsImpl) admin.topics();
+        Field field = TopicsImpl.class.getDeclaredField("adminV2Topics");
         field.setAccessible(true);
         WebTarget persistentTopics = (WebTarget) field.get(persistent);
 
         // (1) Get PartitionedMetadata : with Url and Uri encoding
         final CompletableFuture<PartitionedTopicMetadata> urlEncodedPartitionedMetadata = new CompletableFuture<>();
         // (a) Url encoding
-        persistent.asyncGetRequest(persistentTopics.path(ns1).path(urlEncodedTopic).path("partitions"),
+        persistent.asyncGetRequest(
+                persistentTopics.path("persistent").path(ns1).path(urlEncodedTopic).path("partitions"),
                 new InvocationCallback<PartitionedTopicMetadata>() {
                     @Override
                     public void completed(PartitionedTopicMetadata response) {
@@ -1801,13 +1802,13 @@ public void completed(PartitionedTopicMetadata response) {
 
                     @Override
                     public void failed(Throwable e) {
-                        e.printStackTrace();
-                        Assert.fail(e.getMessage());
+                        urlEncodedPartitionedMetadata.completeExceptionally(e);
                     }
                 });
         final CompletableFuture<PartitionedTopicMetadata> uriEncodedPartitionedMetadata = new CompletableFuture<>();
         // (b) Uri encoding
-        persistent.asyncGetRequest(persistentTopics.path(ns1).path(uriEncodedTopic).path("partitions"),
+        persistent.asyncGetRequest(
+                persistentTopics.path("persistent").path(ns1).path(uriEncodedTopic).path("partitions"),
                 new InvocationCallback<PartitionedTopicMetadata>() {
                     @Override
                     public void completed(PartitionedTopicMetadata response) {
@@ -1839,12 +1840,12 @@ public void failed(Throwable e) {
         assertEquals(urlEncodedLookupData.getBrokerUrl(), uriEncodedLookupData.getBrokerUrl());
 
         // (3) Get Topic Stats
-        final CompletableFuture<PersistentTopicStats> urlStats = new CompletableFuture<>();
+        final CompletableFuture<TopicStats> urlStats = new CompletableFuture<>();
         // (a) Url encoding
-        persistent.asyncGetRequest(persistentTopics.path(ns1).path(urlEncodedTopic + "-partition-1").path("stats"),
-                new InvocationCallback<PersistentTopicStats>() {
+        persistent.asyncGetRequest(persistentTopics.path("persistent").path(ns1).path(urlEncodedTopic + "-partition-1").path("stats"),
+                new InvocationCallback<TopicStats>() {
                     @Override
-                    public void completed(PersistentTopicStats response) {
+                    public void completed(TopicStats response) {
                         urlStats.complete(response);
                     }
 
@@ -1854,11 +1855,12 @@ public void failed(Throwable e) {
                     }
                 });
         // (b) Uri encoding
-        final CompletableFuture<PersistentTopicStats> uriStats = new CompletableFuture<>();
-        persistent.asyncGetRequest(persistentTopics.path(ns1).path(uriEncodedTopic + "-partition-1").path("stats"),
-                new InvocationCallback<PersistentTopicStats>() {
+        final CompletableFuture<TopicStats> uriStats = new CompletableFuture<>();
+        persistent.asyncGetRequest(
+                persistentTopics.path("persistent").path(ns1).path(uriEncodedTopic + "-partition-1").path("stats"),
+                new InvocationCallback<TopicStats>() {
                     @Override
-                    public void completed(PersistentTopicStats response) {
+                    public void completed(TopicStats response) {
                         uriStats.complete(response);
                     }
 
@@ -1927,12 +1929,12 @@ public void testTriggerCompaction() throws Exception {
         CompletableFuture<Long> promise = new CompletableFuture<Long>();
         Compactor compactor = pulsar.getCompactor();
         doReturn(promise).when(compactor).compact(topicName);
-        admin.persistentTopics().triggerCompaction(topicName);
+        admin.topics().triggerCompaction(topicName);
 
         // verify compact called once
         verify(compactor).compact(topicName);
         try {
-            admin.persistentTopics().triggerCompaction(topicName);
+            admin.topics().triggerCompaction(topicName);
 
             fail("Shouldn't be able to run while already running");
         } catch (ConflictException e) {
@@ -1943,7 +1945,7 @@ public void testTriggerCompaction() throws Exception {
 
         // complete first compaction, and trigger again
         promise.complete(1L);
-        admin.persistentTopics().triggerCompaction(topicName);
+        admin.topics().triggerCompaction(topicName);
 
         // verify compact was called again
         verify(compactor, times(2)).compact(topicName);
@@ -1957,26 +1959,26 @@ public void testCompactionStatus() throws Exception {
         pulsarClient.newProducer().topic(topicName).create().close();
         assertNotNull(pulsar.getBrokerService().getTopicReference(topicName));
 
-        assertEquals(admin.persistentTopics().compactionStatus(topicName).status, CompactionStatus.Status.NOT_RUN);
+        assertEquals(admin.topics().compactionStatus(topicName).status, CompactionStatus.Status.NOT_RUN);
 
         // mock actual compaction, we don't need to really run it
         CompletableFuture<Long> promise = new CompletableFuture<Long>();
         Compactor compactor = pulsar.getCompactor();
         doReturn(promise).when(compactor).compact(topicName);
-        admin.persistentTopics().triggerCompaction(topicName);
+        admin.topics().triggerCompaction(topicName);
 
-        assertEquals(admin.persistentTopics().compactionStatus(topicName).status, CompactionStatus.Status.RUNNING);
+        assertEquals(admin.topics().compactionStatus(topicName).status, CompactionStatus.Status.RUNNING);
 
         promise.complete(1L);
 
-        assertEquals(admin.persistentTopics().compactionStatus(topicName).status, CompactionStatus.Status.SUCCESS);
+        assertEquals(admin.topics().compactionStatus(topicName).status, CompactionStatus.Status.SUCCESS);
 
         CompletableFuture<Long> errorPromise = new CompletableFuture<Long>();
         doReturn(errorPromise).when(compactor).compact(topicName);
-        admin.persistentTopics().triggerCompaction(topicName);
+        admin.topics().triggerCompaction(topicName);
         errorPromise.completeExceptionally(new Exception("Failed at something"));
 
-        assertEquals(admin.persistentTopics().compactionStatus(topicName).status, CompactionStatus.Status.ERROR);
-        assertTrue(admin.persistentTopics().compactionStatus(topicName).lastError.contains("Failed at something"));
+        assertEquals(admin.topics().compactionStatus(topicName).status, CompactionStatus.Status.ERROR);
+        assertTrue(admin.topics().compactionStatus(topicName).lastError.contains("Failed at something"));
     }
 }
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest2.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest2.java
index ad95065acb..5c9346e22c 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest2.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/AdminApiTest2.java
@@ -70,7 +70,7 @@
 import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
 import org.apache.pulsar.common.policies.data.PersistencePolicies;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.TenantInfo;
 import org.apache.pulsar.common.policies.data.RetentionPolicies;
 import org.apache.pulsar.common.policies.data.SubscriptionStats;
@@ -146,27 +146,27 @@ public void testIncrementPartitionsOfTopic() throws Exception {
 
         URL pulsarUrl = new URL("http://127.0.0.1" + ":" + BROKER_WEBSERVICE_PORT);
 
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, startPartitions);
+        admin.topics().createPartitionedTopic(partitionedTopicName, startPartitions);
         // validate partition topic is created
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions,
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions,
                 startPartitions);
 
         // create consumer and subscriptions : check subscriptions
         PulsarClient client = PulsarClient.builder().serviceUrl(pulsarUrl.toString()).build();
         Consumer<byte[]> consumer1 = client.newConsumer().topic(partitionedTopicName).subscriptionName(subName1)
                 .subscriptionType(SubscriptionType.Shared).subscribe();
-        assertEquals(admin.persistentTopics().getSubscriptions(partitionedTopicName), Lists.newArrayList(subName1));
+        assertEquals(admin.topics().getSubscriptions(partitionedTopicName), Lists.newArrayList(subName1));
         Consumer<byte[]> consumer2 = client.newConsumer().topic(partitionedTopicName).subscriptionName(subName2)
                 .subscriptionType(SubscriptionType.Shared).subscribe();
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getSubscriptions(partitionedTopicName)),
+        assertEquals(Sets.newHashSet(admin.topics().getSubscriptions(partitionedTopicName)),
                 Sets.newHashSet(subName1, subName2));
 
         // (1) update partitions
-        admin.persistentTopics().updatePartitionedTopic(partitionedTopicName, newPartitions);
+        admin.topics().updatePartitionedTopic(partitionedTopicName, newPartitions);
         // invalidate global-cache to make sure that mock-zk-cache reds fresh data
         pulsar.getGlobalZkCache().invalidateAll();
         // verify new partitions have been created
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions,
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions,
                 newPartitions);
         // (2) No Msg loss: verify new partitions have the same existing subscription names
         final String newPartitionTopicName = TopicName.get(partitionedTopicName).getPartition(startPartitions + 1)
@@ -191,13 +191,13 @@ public void testIncrementPartitionsOfTopic() throws Exception {
                 .subscriptionType(SubscriptionType.Shared).subscribe();
         // sometime: mockZk fails to refresh ml-cache: so, invalidate the cache to get fresh data
         pulsar.getLocalZkCacheService().managedLedgerListCache().clearTree();
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getSubscriptions(newPartitionTopicName)),
+        assertEquals(Sets.newHashSet(admin.topics().getSubscriptions(newPartitionTopicName)),
                 Sets.newHashSet(subName1, subName2));
 
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getList("prop-xyz/ns1")).size(), newPartitions);
+        assertEquals(Sets.newHashSet(admin.topics().getList("prop-xyz/ns1")).size(), newPartitions);
 
         // test cumulative stats for partitioned topic
-        PartitionedTopicStats topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, false);
+        PartitionedTopicStats topicStats = admin.topics().getPartitionedStats(partitionedTopicName, false);
         assertEquals(topicStats.subscriptions.keySet(), Sets.newTreeSet(Lists.newArrayList(subName1, subName2)));
         assertEquals(topicStats.subscriptions.get(subName2).consumers.size(), 1);
         assertEquals(topicStats.subscriptions.get(subName2).msgBacklog, totalMessages);
@@ -205,7 +205,7 @@ public void testIncrementPartitionsOfTopic() throws Exception {
         assertEquals(topicStats.partitions, Maps.newHashMap());
 
         // (5) verify: each partition should have backlog
-        topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, true);
+        topicStats = admin.topics().getPartitionedStats(partitionedTopicName, true);
         assertEquals(topicStats.metadata.partitions, newPartitions);
         Set<String> partitionSet = Sets.newHashSet();
         for (int i = 0; i < newPartitions; i++) {
@@ -213,7 +213,7 @@ public void testIncrementPartitionsOfTopic() throws Exception {
         }
         assertEquals(topicStats.partitions.keySet(), partitionSet);
         for (int i = 0; i < newPartitions; i++) {
-            PersistentTopicStats partitionStats = topicStats.partitions
+            TopicStats partitionStats = topicStats.partitions
                     .get(TopicName.get(partitionedTopicName).getPartition(i).toString());
             assertEquals(partitionStats.publishers.size(), 1);
             assertEquals(partitionStats.subscriptions.get(subName2).consumers.size(), 1);
@@ -248,26 +248,26 @@ public void nonPersistentTopics() throws Exception {
 
         publishMessagesOnTopic("non-persistent://prop-xyz/ns1/" + topicName, 10, 0);
 
-        NonPersistentTopicStats topicStats = admin.nonPersistentTopics().getStats(persistentTopicName);
-        assertEquals(topicStats.getSubscriptions().keySet(), Sets.newTreeSet(Lists.newArrayList("my-sub")));
-        assertEquals(topicStats.getSubscriptions().get("my-sub").consumers.size(), 1);
-        assertEquals(topicStats.getPublishers().size(), 0);
+        TopicStats topicStats = admin.topics().getStats(persistentTopicName);
+        assertEquals(topicStats.subscriptions.keySet(), Sets.newTreeSet(Lists.newArrayList("my-sub")));
+        assertEquals(topicStats.subscriptions.get("my-sub").consumers.size(), 1);
+        assertEquals(topicStats.publishers.size(), 0);
 
-        PersistentTopicInternalStats internalStats = admin.nonPersistentTopics().getInternalStats(persistentTopicName);
+        PersistentTopicInternalStats internalStats = admin.topics().getInternalStats(persistentTopicName);
         assertEquals(internalStats.cursors.keySet(), Sets.newTreeSet(Lists.newArrayList("my-sub")));
 
         consumer.close();
         client.close();
 
-        topicStats = admin.nonPersistentTopics().getStats(persistentTopicName);
-        assertTrue(topicStats.getSubscriptions().keySet().contains("my-sub"));
-        assertEquals(topicStats.getPublishers().size(), 0);
+        topicStats = admin.topics().getStats(persistentTopicName);
+        assertTrue(topicStats.subscriptions.keySet().contains("my-sub"));
+        assertEquals(topicStats.publishers.size(), 0);
 
         // test partitioned-topic
         final String partitionedTopicName = "non-persistent://prop-xyz/ns1/paritioned";
-        assertEquals(admin.nonPersistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 0);
-        admin.nonPersistentTopics().createPartitionedTopic(partitionedTopicName, 5);
-        assertEquals(admin.nonPersistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 5);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 0);
+        admin.topics().createPartitionedTopic(partitionedTopicName, 5);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 5);
     }
 
     private void publishMessagesOnTopic(String topicName, int messages, int startIdx) throws Exception {
@@ -409,11 +409,7 @@ public void testUnloadTopic(final String topicType) throws Exception {
     }
 
     private void unloadTopic(String topicName, boolean isPersistentTopic) throws Exception {
-        if (isPersistentTopic) {
-            admin.persistentTopics().unload(topicName);
-        } else {
-            admin.nonPersistentTopics().unload(topicName);
-        }
+        admin.topics().unload(topicName);
     }
 
     /**
@@ -441,11 +437,11 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Shared).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, totalProducedMessages, 0);
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(topicName, "my-sub", 10);
         assertEquals(messages.size(), 10);
 
         Message<byte[]> message = null;
@@ -465,7 +461,7 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
         // messages should still be available due to retention
         MessageIdImpl messageId = new MessageIdImpl(resetMessageId.getLedgerId(), resetMessageId.getEntryId(), -1);
         // reset position at resetMessageId
-        admin.persistentTopics().resetCursor(topicName, "my-sub", messageId);
+        admin.topics().resetCursor(topicName, "my-sub", messageId);
 
         consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Shared).subscribe();
@@ -486,7 +482,7 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
 
         // invalid topic name
         try {
-            admin.persistentTopics().resetCursor(topicName + "invalid", "my-sub", messageId);
+            admin.topics().resetCursor(topicName + "invalid", "my-sub", messageId);
             fail("It should have failed due to invalid topic name");
         } catch (PulsarAdminException.NotFoundException e) {
             // Ok
@@ -494,7 +490,7 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
 
         // invalid cursor name
         try {
-            admin.persistentTopics().resetCursor(topicName, "invalid-sub", messageId);
+            admin.topics().resetCursor(topicName, "invalid-sub", messageId);
             fail("It should have failed due to invalid subscription name");
         } catch (PulsarAdminException.NotFoundException e) {
             // Ok
@@ -503,7 +499,7 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
         // invalid position
         try {
             messageId = new MessageIdImpl(0, 0, -1);
-            admin.persistentTopics().resetCursor(topicName, "my-sub", messageId);
+            admin.topics().resetCursor(topicName, "my-sub", messageId);
             fail("It should have failed due to invalid subscription name");
         } catch (PulsarAdminException.PreconditionFailedException e) {
             // Ok
@@ -738,7 +734,7 @@ public void testNonPersistentTopics() throws Exception {
             assertNotNull(topic);
         }
 
-        Set<String> topicsInNs = Sets.newHashSet(admin.nonPersistentTopics().getList(namespace));
+        Set<String> topicsInNs = Sets.newHashSet(admin.topics().getList(namespace));
         assertEquals(topicsInNs.size(), totalTopics);
         topicsInNs.removeAll(topicNames);
         assertEquals(topicsInNs.size(), 0);
@@ -764,9 +760,9 @@ public void testPublishConsumerStats() throws Exception {
             .create();
 
         retryStrategically((test) -> {
-            PersistentTopicStats stats;
+            TopicStats stats;
             try {
-                stats = admin.persistentTopics().getStats(topic);
+                stats = admin.topics().getStats(topic);
                 return stats.publishers.size() > 0 && stats.subscriptions.get(subscriberName) != null
                         && stats.subscriptions.get(subscriberName).consumers.size() > 0;
             } catch (PulsarAdminException e) {
@@ -774,7 +770,7 @@ public void testPublishConsumerStats() throws Exception {
             }
         }, 5, 200);
 
-        PersistentTopicStats topicStats = admin.persistentTopics().getStats(topic);
+        TopicStats topicStats = admin.topics().getStats(topic);
         assertEquals(topicStats.publishers.size(), 1);
         assertNotNull(topicStats.publishers.get(0).getAddress());
         assertNotNull(topicStats.publishers.get(0).getClientVersion());
@@ -806,7 +802,7 @@ public void testTenantNameWithUnderscore() throws Exception {
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topic)
                 .create();
 
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topic);
+        TopicStats stats = admin.topics().getStats(topic);
         assertEquals(stats.publishers.size(), 1);
         producer.close();
     }
@@ -830,7 +826,7 @@ public void testTenantNameWithInvalidCharacters() throws Exception {
             // Expected
         }
     }
-    
+
     @Test
     public void brokerNamespaceIsolationPolicies() throws Exception {
 
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/CreateSubscriptionTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/CreateSubscriptionTest.java
index ec719150cb..0cf2c5ca91 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/CreateSubscriptionTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/CreateSubscriptionTest.java
@@ -21,10 +21,11 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.fail;
 
+import com.google.common.collect.Lists;
+
 import javax.ws.rs.ClientErrorException;
 import javax.ws.rs.core.Response.Status;
 
-import org.apache.pulsar.broker.auth.MockedPulsarServiceBaseTest;
 import org.apache.pulsar.client.admin.PulsarAdminException.ConflictException;
 import org.apache.pulsar.client.api.MessageId;
 import org.apache.pulsar.client.api.Producer;
@@ -34,8 +35,6 @@
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Lists;
-
 public class CreateSubscriptionTest extends ProducerConsumerBase {
 
     @BeforeMethod
@@ -54,53 +53,53 @@ public void cleanup() throws Exception {
     @Test
     public void createSubscriptionSingleTopic() throws Exception {
         String topic = "persistent://my-property/my-ns/my-topic";
-        admin.persistentTopics().createSubscription(topic, "sub-1", MessageId.latest);
+        admin.topics().createSubscription(topic, "sub-1", MessageId.latest);
 
         // Create should fail if the subscription already exists
         try {
-            admin.persistentTopics().createSubscription(topic, "sub-1", MessageId.latest);
+            admin.topics().createSubscription(topic, "sub-1", MessageId.latest);
             fail("Should have failed");
         } catch (ConflictException e) {
             assertEquals(((ClientErrorException) e.getCause()).getResponse().getStatus(),
                     Status.CONFLICT.getStatusCode());
         }
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topic), Lists.newArrayList("sub-1"));
+        assertEquals(admin.topics().getSubscriptions(topic), Lists.newArrayList("sub-1"));
 
         Producer<byte[]> p1 = pulsarClient.newProducer().topic(topic).create();
         p1.send("test-1".getBytes());
         p1.send("test-2".getBytes());
         MessageId m3 = p1.send("test-3".getBytes());
 
-        assertEquals(admin.persistentTopics().getStats(topic).subscriptions.get("sub-1").msgBacklog, 3);
+        assertEquals(admin.topics().getStats(topic).subscriptions.get("sub-1").msgBacklog, 3);
 
-        admin.persistentTopics().createSubscription(topic, "sub-2", MessageId.latest);
-        assertEquals(admin.persistentTopics().getStats(topic).subscriptions.get("sub-2").msgBacklog, 0);
+        admin.topics().createSubscription(topic, "sub-2", MessageId.latest);
+        assertEquals(admin.topics().getStats(topic).subscriptions.get("sub-2").msgBacklog, 0);
 
-        admin.persistentTopics().createSubscription(topic, "sub-3", MessageId.earliest);
-        assertEquals(admin.persistentTopics().getStats(topic).subscriptions.get("sub-3").msgBacklog, 3);
+        admin.topics().createSubscription(topic, "sub-3", MessageId.earliest);
+        assertEquals(admin.topics().getStats(topic).subscriptions.get("sub-3").msgBacklog, 3);
 
-        admin.persistentTopics().createSubscription(topic, "sub-5", m3);
-        assertEquals(admin.persistentTopics().getStats(topic).subscriptions.get("sub-5").msgBacklog, 1);
+        admin.topics().createSubscription(topic, "sub-5", m3);
+        assertEquals(admin.topics().getStats(topic).subscriptions.get("sub-5").msgBacklog, 1);
     }
 
     @Test
     public void createSubscriptionOnPartitionedTopic() throws Exception {
         String topic = "persistent://my-property/my-ns/my-partitioned-topic";
-        admin.persistentTopics().createPartitionedTopic(topic, 10);
+        admin.topics().createPartitionedTopic(topic, 10);
 
-        admin.persistentTopics().createSubscription(topic, "sub-1", MessageId.latest);
+        admin.topics().createSubscription(topic, "sub-1", MessageId.latest);
 
         // Create should fail if the subscription already exists
         try {
-            admin.persistentTopics().createSubscription(topic, "sub-1", MessageId.latest);
+            admin.topics().createSubscription(topic, "sub-1", MessageId.latest);
             fail("Should have failed");
         } catch (Exception e) {
             // Expected
         }
 
         for (int i = 0; i < 10; i++) {
-            assertEquals(admin.persistentTopics().getSubscriptions(TopicName.get(topic).getPartition(i).toString()),
+            assertEquals(admin.topics().getSubscriptions(TopicName.get(topic).getPartition(i).toString()),
                     Lists.newArrayList("sub-1"));
         }
     }
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/IncrementPartitionsTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/IncrementPartitionsTest.java
index 6ccce68a2d..02430c8c68 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/IncrementPartitionsTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/IncrementPartitionsTest.java
@@ -66,27 +66,27 @@ public void cleanup() throws Exception {
     public void testIncrementPartitionsOfTopicOnUnusedTopic() throws Exception {
         final String partitionedTopicName = "persistent://prop-xyz/use/ns1/test-topic";
 
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 10);
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 10);
+        admin.topics().createPartitionedTopic(partitionedTopicName, 10);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 10);
 
-        admin.persistentTopics().updatePartitionedTopic(partitionedTopicName, 20);
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 20);
+        admin.topics().updatePartitionedTopic(partitionedTopicName, 20);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 20);
     }
 
     @Test
     public void testIncrementPartitionsOfTopic() throws Exception {
         final String partitionedTopicName = "persistent://prop-xyz/use/ns1/test-topic-2";
 
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 10);
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 10);
+        admin.topics().createPartitionedTopic(partitionedTopicName, 10);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 10);
 
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(partitionedTopicName).subscriptionName("sub-1")
                 .subscribe();
 
-        admin.persistentTopics().updatePartitionedTopic(partitionedTopicName, 20);
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 20);
+        admin.topics().updatePartitionedTopic(partitionedTopicName, 20);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 20);
 
-        assertEquals(admin.persistentTopics().getSubscriptions(
+        assertEquals(admin.topics().getSubscriptions(
                 TopicName.get(partitionedTopicName).getPartition(15).toString()), Lists.newArrayList("sub-1"));
 
         consumer.close();
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java
index 1df9b7fd1b..305c887f91 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java
@@ -1106,7 +1106,7 @@ public void testDeleteNamespace() throws Exception {
         producer.close();
         NamespaceBundle bundle1 = pulsar.getNamespaceService().getBundle(topic);
         // (2) Delete topic
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
         // (3) Delete ns
         admin.namespaces().deleteNamespace(namespace);
         // (4) check bundle
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/v1/V1_AdminApiTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/v1/V1_AdminApiTest.java
index 2ca1e08690..7a514cddc5 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/v1/V1_AdminApiTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/v1/V1_AdminApiTest.java
@@ -61,7 +61,7 @@
 import org.apache.pulsar.client.admin.PulsarAdminException.NotFoundException;
 import org.apache.pulsar.client.admin.PulsarAdminException.PreconditionFailedException;
 import org.apache.pulsar.client.admin.internal.LookupImpl;
-import org.apache.pulsar.client.admin.internal.PersistentTopicsImpl;
+import org.apache.pulsar.client.admin.internal.TopicsImpl;
 import org.apache.pulsar.client.admin.internal.TenantsImpl;
 import org.apache.pulsar.client.api.Consumer;
 import org.apache.pulsar.client.api.ConsumerBuilder;
@@ -92,7 +92,7 @@
 import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
 import org.apache.pulsar.common.policies.data.PersistencePolicies;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.Policies;
 import org.apache.pulsar.common.policies.data.TenantInfo;
 import org.apache.pulsar.common.policies.data.RetentionPolicies;
@@ -643,7 +643,7 @@ public void namespaces() throws PulsarAdminException, PulsarServerException, Exc
         // Force topic creation and namespace being loaded
         Producer<byte[]> producer = pulsarClient.newProducer().topic("persistent://prop-xyz/use/ns1/my-topic").create();
         producer.close();
-        admin.persistentTopics().delete("persistent://prop-xyz/use/ns1/my-topic");
+        admin.topics().delete("persistent://prop-xyz/use/ns1/my-topic");
 
         admin.namespaces().unloadNamespaceBundle("prop-xyz/use/ns1", "0x00000000_0xffffffff");
         NamespaceName ns = NamespaceName.get("prop-xyz/use/ns1");
@@ -676,7 +676,7 @@ public void namespaces() throws PulsarAdminException, PulsarServerException, Exc
         // Force topic creation and namespace being loaded
         producer = pulsarClient.newProducer().topic("persistent://prop-xyz/use/ns2/my-topic").create();
         producer.close();
-        admin.persistentTopics().delete("persistent://prop-xyz/use/ns2/my-topic");
+        admin.topics().delete("persistent://prop-xyz/use/ns2/my-topic");
 
         // both unload and delete should succeed for ns2 on other broker with a redirect
         // otheradmin.namespaces().unload("prop-xyz/use/ns2");
@@ -684,12 +684,12 @@ public void namespaces() throws PulsarAdminException, PulsarServerException, Exc
 
     @Test(dataProvider = "topicName")
     public void persistentTopics(String topicName) throws Exception {
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
 
         final String persistentTopicName = "persistent://prop-xyz/use/ns1/" + topicName;
         // Force to create a topic
         publishMessagesOnPersistentTopic("persistent://prop-xyz/use/ns1/" + topicName, 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"),
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"),
                 Lists.newArrayList("persistent://prop-xyz/use/ns1/" + topicName));
 
         // create consumer and subscription
@@ -699,83 +699,83 @@ public void persistentTopics(String topicName) throws Exception {
         Consumer<byte[]> consumer = client.newConsumer().topic(persistentTopicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(persistentTopicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(persistentTopicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic("persistent://prop-xyz/use/ns1/" + topicName, 10);
 
-        PersistentTopicStats topicStats = admin.persistentTopics().getStats(persistentTopicName);
+        TopicStats topicStats = admin.topics().getStats(persistentTopicName);
         assertEquals(topicStats.subscriptions.keySet(), Sets.newTreeSet(Lists.newArrayList("my-sub")));
         assertEquals(topicStats.subscriptions.get("my-sub").consumers.size(), 1);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 10);
         assertEquals(topicStats.publishers.size(), 0);
 
-        PersistentTopicInternalStats internalStats = admin.persistentTopics().getInternalStats(persistentTopicName);
+        PersistentTopicInternalStats internalStats = admin.topics().getInternalStats(persistentTopicName);
         assertEquals(internalStats.cursors.keySet(), Sets.newTreeSet(Lists.newArrayList("my-sub")));
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(persistentTopicName, "my-sub", 3);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(persistentTopicName, "my-sub", 3);
         assertEquals(messages.size(), 3);
         for (int i = 0; i < 3; i++) {
             String expectedMessage = "message-" + i;
             assertEquals(messages.get(i).getData(), expectedMessage.getBytes());
         }
 
-        messages = admin.persistentTopics().peekMessages(persistentTopicName, "my-sub", 15);
+        messages = admin.topics().peekMessages(persistentTopicName, "my-sub", 15);
         assertEquals(messages.size(), 10);
         for (int i = 0; i < 10; i++) {
             String expectedMessage = "message-" + i;
             assertEquals(messages.get(i).getData(), expectedMessage.getBytes());
         }
 
-        admin.persistentTopics().skipMessages(persistentTopicName, "my-sub", 5);
-        topicStats = admin.persistentTopics().getStats(persistentTopicName);
+        admin.topics().skipMessages(persistentTopicName, "my-sub", 5);
+        topicStats = admin.topics().getStats(persistentTopicName);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 5);
 
-        admin.persistentTopics().skipAllMessages(persistentTopicName, "my-sub");
-        topicStats = admin.persistentTopics().getStats(persistentTopicName);
+        admin.topics().skipAllMessages(persistentTopicName, "my-sub");
+        topicStats = admin.topics().getStats(persistentTopicName);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 0);
 
         consumer.close();
         client.close();
 
-        admin.persistentTopics().deleteSubscription(persistentTopicName, "my-sub");
+        admin.topics().deleteSubscription(persistentTopicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(persistentTopicName), Lists.newArrayList());
-        topicStats = admin.persistentTopics().getStats(persistentTopicName);
+        assertEquals(admin.topics().getSubscriptions(persistentTopicName), Lists.newArrayList());
+        topicStats = admin.topics().getStats(persistentTopicName);
         assertEquals(topicStats.subscriptions.keySet(), Sets.newTreeSet());
         assertEquals(topicStats.publishers.size(), 0);
 
         try {
-            admin.persistentTopics().skipAllMessages(persistentTopicName, "my-sub");
+            admin.topics().skipAllMessages(persistentTopicName, "my-sub");
         } catch (NotFoundException e) {
         }
 
-        admin.persistentTopics().delete(persistentTopicName);
+        admin.topics().delete(persistentTopicName);
 
         try {
-            admin.persistentTopics().delete(persistentTopicName);
+            admin.topics().delete(persistentTopicName);
             fail("Should have received 404");
         } catch (NotFoundException e) {
         }
 
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
     }
 
     @Test(dataProvider = "topicName")
     public void partitionedTopics(String topicName) throws Exception {
-        assertEquals(admin.persistentTopics().getPartitionedTopicList("prop-xyz/use/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getPartitionedTopicList("prop-xyz/use/ns1"), Lists.newArrayList());
         final String partitionedTopicName = "persistent://prop-xyz/use/ns1/" + topicName;
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 4);
-        assertEquals(admin.persistentTopics().getPartitionedTopicList("prop-xyz/use/ns1"),
+        admin.topics().createPartitionedTopic(partitionedTopicName, 4);
+        assertEquals(admin.topics().getPartitionedTopicList("prop-xyz/use/ns1"),
                 Lists.newArrayList(partitionedTopicName));
 
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 4);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 4);
 
         // check if the virtual topic doesn't get created
-        List<String> topics = admin.persistentTopics().getList("prop-xyz/use/ns1");
+        List<String> topics = admin.topics().getList("prop-xyz/use/ns1");
         assertEquals(topics.size(), 0);
 
         assertEquals(
-                admin.persistentTopics().getPartitionedTopicMetadata("persistent://prop-xyz/use/ns1/ds2").partitions,
+                admin.topics().getPartitionedTopicMetadata("persistent://prop-xyz/use/ns1/ds2").partitions,
                 0);
 
         // create consumer and subscription
@@ -785,10 +785,10 @@ public void partitionedTopics(String topicName) throws Exception {
         Consumer<byte[]> consumer = client.newConsumer().topic(partitionedTopicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(partitionedTopicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(partitionedTopicName), Lists.newArrayList("my-sub"));
 
         try {
-            admin.persistentTopics().deleteSubscription(partitionedTopicName, "my-sub");
+            admin.topics().deleteSubscription(partitionedTopicName, "my-sub");
             fail("should have failed");
         } catch (PulsarAdminException.PreconditionFailedException e) {
             // ok
@@ -799,12 +799,12 @@ public void partitionedTopics(String topicName) throws Exception {
         Consumer<byte[]> consumer1 = client.newConsumer().topic(partitionedTopicName).subscriptionName("my-sub-1")
                 .subscribe();
 
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getSubscriptions(partitionedTopicName)),
+        assertEquals(Sets.newHashSet(admin.topics().getSubscriptions(partitionedTopicName)),
                 Sets.newHashSet("my-sub", "my-sub-1"));
 
         consumer1.close();
-        admin.persistentTopics().deleteSubscription(partitionedTopicName, "my-sub-1");
-        assertEquals(admin.persistentTopics().getSubscriptions(partitionedTopicName), Lists.newArrayList("my-sub"));
+        admin.topics().deleteSubscription(partitionedTopicName, "my-sub-1");
+        assertEquals(admin.topics().getSubscriptions(partitionedTopicName), Lists.newArrayList("my-sub"));
 
         Producer<byte[]> producer = client.newProducer()
             .topic(partitionedTopicName)
@@ -817,12 +817,12 @@ public void partitionedTopics(String topicName) throws Exception {
             producer.send(message.getBytes());
         }
 
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getList("prop-xyz/use/ns1")),
+        assertEquals(Sets.newHashSet(admin.topics().getList("prop-xyz/use/ns1")),
                 Sets.newHashSet(partitionedTopicName + "-partition-0", partitionedTopicName + "-partition-1",
                         partitionedTopicName + "-partition-2", partitionedTopicName + "-partition-3"));
 
         // test cumulative stats for partitioned topic
-        PartitionedTopicStats topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, false);
+        PartitionedTopicStats topicStats = admin.topics().getPartitionedStats(partitionedTopicName, false);
         assertEquals(topicStats.subscriptions.keySet(), Sets.newTreeSet(Lists.newArrayList("my-sub")));
         assertEquals(topicStats.subscriptions.get("my-sub").consumers.size(), 1);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 10);
@@ -830,36 +830,36 @@ public void partitionedTopics(String topicName) throws Exception {
         assertEquals(topicStats.partitions, Maps.newHashMap());
 
         // test per partition stats for partitioned topic
-        topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, true);
+        topicStats = admin.topics().getPartitionedStats(partitionedTopicName, true);
         assertEquals(topicStats.metadata.partitions, 4);
         assertEquals(topicStats.partitions.keySet(),
                 Sets.newHashSet(partitionedTopicName + "-partition-0", partitionedTopicName + "-partition-1",
                         partitionedTopicName + "-partition-2", partitionedTopicName + "-partition-3"));
-        PersistentTopicStats partitionStats = topicStats.partitions.get(partitionedTopicName + "-partition-0");
+        TopicStats partitionStats = topicStats.partitions.get(partitionedTopicName + "-partition-0");
         assertEquals(partitionStats.publishers.size(), 1);
         assertEquals(partitionStats.subscriptions.get("my-sub").consumers.size(), 1);
         assertEquals(partitionStats.subscriptions.get("my-sub").msgBacklog, 3, 1);
 
         try {
-            admin.persistentTopics().skipMessages(partitionedTopicName, "my-sub", 5);
+            admin.topics().skipMessages(partitionedTopicName, "my-sub", 5);
             fail("skip messages for partitioned topics should fail");
         } catch (Exception e) {
             // ok
         }
 
-        admin.persistentTopics().skipAllMessages(partitionedTopicName, "my-sub");
-        topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, false);
+        admin.topics().skipAllMessages(partitionedTopicName, "my-sub");
+        topicStats = admin.topics().getPartitionedStats(partitionedTopicName, false);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 0);
 
         producer.close();
         consumer.close();
 
-        admin.persistentTopics().deleteSubscription(partitionedTopicName, "my-sub");
+        admin.topics().deleteSubscription(partitionedTopicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(partitionedTopicName), Lists.newArrayList());
+        assertEquals(admin.topics().getSubscriptions(partitionedTopicName), Lists.newArrayList());
 
         try {
-            admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 32);
+            admin.topics().createPartitionedTopic(partitionedTopicName, 32);
             fail("Should have failed as the partitioned topic already exists");
         } catch (ConflictException ce) {
         }
@@ -870,11 +870,11 @@ public void partitionedTopics(String topicName) throws Exception {
             .messageRoutingMode(MessageRoutingMode.SinglePartition)
             .create();
 
-        topics = admin.persistentTopics().getList("prop-xyz/use/ns1");
+        topics = admin.topics().getList("prop-xyz/use/ns1");
         assertEquals(topics.size(), 4);
 
         try {
-            admin.persistentTopics().deletePartitionedTopic(partitionedTopicName);
+            admin.topics().deletePartitionedTopic(partitionedTopicName);
             fail("The topic is busy");
         } catch (PreconditionFailedException pfe) {
             // ok
@@ -883,25 +883,25 @@ public void partitionedTopics(String topicName) throws Exception {
         producer.close();
         client.close();
 
-        admin.persistentTopics().deletePartitionedTopic(partitionedTopicName);
+        admin.topics().deletePartitionedTopic(partitionedTopicName);
 
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 0);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 0);
 
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 32);
+        admin.topics().createPartitionedTopic(partitionedTopicName, 32);
 
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 32);
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions, 32);
 
         try {
-            admin.persistentTopics().deletePartitionedTopic("persistent://prop-xyz/use/ns1/ds2");
+            admin.topics().deletePartitionedTopic("persistent://prop-xyz/use/ns1/ds2");
             fail("Should have failed as the partitioned topic was not created");
         } catch (NotFoundException nfe) {
         }
 
-        admin.persistentTopics().deletePartitionedTopic(partitionedTopicName);
+        admin.topics().deletePartitionedTopic(partitionedTopicName);
 
         // delete a partitioned topic in a global namespace
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, 4);
-        admin.persistentTopics().deletePartitionedTopic(partitionedTopicName);
+        admin.topics().createPartitionedTopic(partitionedTopicName, 4);
+        admin.topics().deletePartitionedTopic(partitionedTopicName);
     }
 
     @Test(dataProvider = "numBundles")
@@ -933,7 +933,7 @@ public void testNamespaceSplitBundle() throws Exception {
             .create();
         producer.send("message".getBytes());
         publishMessagesOnPersistentTopic(topicName, 0);
-        assertEquals(admin.persistentTopics().getList(namespace), Lists.newArrayList(topicName));
+        assertEquals(admin.topics().getList(namespace), Lists.newArrayList(topicName));
 
         try {
             admin.namespaces().splitNamespaceBundle(namespace, "0x00000000_0xffffffff", true);
@@ -963,7 +963,7 @@ public void testNamespaceSplitBundleConcurrent() throws Exception {
             .create();
         producer.send("message".getBytes());
         publishMessagesOnPersistentTopic(topicName, 0);
-        assertEquals(admin.persistentTopics().getList(namespace), Lists.newArrayList(topicName));
+        assertEquals(admin.topics().getList(namespace), Lists.newArrayList(topicName));
 
         try {
             admin.namespaces().splitNamespaceBundle(namespace, "0x00000000_0xffffffff", false);
@@ -1066,17 +1066,17 @@ public void testNamespaceSplitBundleConcurrent() throws Exception {
 
     @Test
     public void testNamespaceUnloadBundle() throws Exception {
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
 
         // Force to create a topic
         publishMessagesOnPersistentTopic("persistent://prop-xyz/use/ns1/ds2", 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"),
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"),
                 Lists.newArrayList("persistent://prop-xyz/use/ns1/ds2"));
 
         // create consumer and subscription
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic("persistent://prop-xyz/use/ns1/ds2")
                 .subscriptionName("my-sub").subscribe();
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/use/ns1/ds2"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/use/ns1/ds2"),
                 Lists.newArrayList("my-sub"));
 
         // Create producer
@@ -1111,7 +1111,7 @@ public void testNamespaceUnloadBundle() throws Exception {
         // Force reload of namespace and wait for topic to be ready
         for (int i = 0; i < 30; i++) {
             try {
-                admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1/ds2");
+                admin.topics().getStats("persistent://prop-xyz/use/ns1/ds2");
                 break;
             } catch (PulsarAdminException e) {
                 LOG.warn("Failed to get topic stats.. {}", e.getMessage());
@@ -1119,25 +1119,25 @@ public void testNamespaceUnloadBundle() throws Exception {
             }
         }
 
-        admin.persistentTopics().deleteSubscription("persistent://prop-xyz/use/ns1/ds2", "my-sub");
-        admin.persistentTopics().delete("persistent://prop-xyz/use/ns1/ds2");
+        admin.topics().deleteSubscription("persistent://prop-xyz/use/ns1/ds2", "my-sub");
+        admin.topics().delete("persistent://prop-xyz/use/ns1/ds2");
     }
 
     @Test(dataProvider = "numBundles")
     public void testNamespaceBundleUnload(Integer numBundles) throws Exception {
         admin.namespaces().createNamespace("prop-xyz/use/ns1-bundles", numBundles);
 
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1-bundles"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1-bundles"), Lists.newArrayList());
 
         // Force to create a topic
         publishMessagesOnPersistentTopic("persistent://prop-xyz/use/ns1-bundles/ds2", 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1-bundles"),
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1-bundles"),
                 Lists.newArrayList("persistent://prop-xyz/use/ns1-bundles/ds2"));
 
         // create consumer and subscription
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic("persistent://prop-xyz/use/ns1-bundles/ds2")
                 .subscriptionName("my-sub").subscribe();
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds2"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds2"),
                 Lists.newArrayList("my-sub"));
 
         // Create producer
@@ -1168,7 +1168,7 @@ public void testNamespaceBundleUnload(Integer numBundles) throws Exception {
         // Force reload of namespace and wait for topic to be ready
         for (int i = 0; i < 30; i++) {
             try {
-                admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1-bundles/ds2");
+                admin.topics().getStats("persistent://prop-xyz/use/ns1-bundles/ds2");
                 break;
             } catch (PulsarAdminException e) {
                 LOG.warn("Failed to get topic stats.. {}", e.getMessage());
@@ -1176,8 +1176,8 @@ public void testNamespaceBundleUnload(Integer numBundles) throws Exception {
             }
         }
 
-        admin.persistentTopics().deleteSubscription("persistent://prop-xyz/use/ns1-bundles/ds2", "my-sub");
-        admin.persistentTopics().delete("persistent://prop-xyz/use/ns1-bundles/ds2");
+        admin.topics().deleteSubscription("persistent://prop-xyz/use/ns1-bundles/ds2", "my-sub");
+        admin.topics().delete("persistent://prop-xyz/use/ns1-bundles/ds2");
     }
 
     @Test(dataProvider = "bundling")
@@ -1224,25 +1224,25 @@ public void testClearBacklogOnNamespace(Integer numBundles) throws Exception {
 
         admin.namespaces().clearNamespaceBacklogForSubscription("prop-xyz/use/ns1-bundles", "my-sub");
 
-        long backlog = admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1-bundles/ds2").subscriptions
+        long backlog = admin.topics().getStats("persistent://prop-xyz/use/ns1-bundles/ds2").subscriptions
                 .get("my-sub").msgBacklog;
         assertEquals(backlog, 0);
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1-bundles/ds1").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/use/ns1-bundles/ds1").subscriptions
                 .get("my-sub").msgBacklog;
         assertEquals(backlog, 0);
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1-bundles/ds1").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/use/ns1-bundles/ds1").subscriptions
                 .get("my-sub-1").msgBacklog;
         assertEquals(backlog, 10);
 
         admin.namespaces().clearNamespaceBacklog("prop-xyz/use/ns1-bundles");
 
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1-bundles/ds1").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/use/ns1-bundles/ds1").subscriptions
                 .get("my-sub-1").msgBacklog;
         assertEquals(backlog, 0);
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1-bundles/ds2").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/use/ns1-bundles/ds2").subscriptions
                 .get("my-sub-1").msgBacklog;
         assertEquals(backlog, 0);
-        backlog = admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1-bundles/ds2").subscriptions
+        backlog = admin.topics().getStats("persistent://prop-xyz/use/ns1-bundles/ds2").subscriptions
                 .get("my-sub-2").msgBacklog;
         assertEquals(backlog, 0);
     }
@@ -1283,9 +1283,9 @@ public void testUnsubscribeOnNamespace(Integer numBundles) throws Exception {
 
         admin.namespaces().unsubscribeNamespace("prop-xyz/use/ns1-bundles", "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds2"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds2"),
                 Lists.newArrayList("my-sub-1", "my-sub-2"));
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds1"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds1"),
                 Lists.newArrayList("my-sub-1"));
 
         consumer2.close();
@@ -1293,9 +1293,9 @@ public void testUnsubscribeOnNamespace(Integer numBundles) throws Exception {
 
         admin.namespaces().unsubscribeNamespace("prop-xyz/use/ns1-bundles", "my-sub-1");
 
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds2"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds2"),
                 Lists.newArrayList("my-sub-2"));
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds1"),
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/use/ns1-bundles/ds1"),
                 Lists.newArrayList());
     }
 
@@ -1346,7 +1346,7 @@ public void backlogQuotas() throws Exception {
     @Test
     public void statsOnNonExistingTopics() throws Exception {
         try {
-            admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1/ghostTopic");
+            admin.topics().getStats("persistent://prop-xyz/use/ns1/ghostTopic");
             fail("The topic doesn't exist");
         } catch (NotFoundException e) {
             // OK
@@ -1363,7 +1363,7 @@ public void testDeleteFailedReturnCode() throws Exception {
             .create();
 
         try {
-            admin.persistentTopics().delete(topicName);
+            admin.topics().delete(topicName);
             fail("The topic is busy");
         } catch (PreconditionFailedException e) {
             // OK
@@ -1374,14 +1374,14 @@ public void testDeleteFailedReturnCode() throws Exception {
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("sub").subscribe();
 
         try {
-            admin.persistentTopics().delete(topicName);
+            admin.topics().delete(topicName);
             fail("The topic is busy");
         } catch (PreconditionFailedException e) {
             // OK
         }
 
         try {
-            admin.persistentTopics().deleteSubscription(topicName, "sub");
+            admin.topics().deleteSubscription(topicName, "sub");
             fail("The topic is busy");
         } catch (PreconditionFailedException e) {
             // Ok
@@ -1390,7 +1390,7 @@ public void testDeleteFailedReturnCode() throws Exception {
         consumer.close();
 
         // Now should succeed
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
     }
 
     private static class IncompatiblePropertyAdmin {
@@ -1435,7 +1435,7 @@ public void testBackwardCompatiblity() throws Exception {
     public void persistentTopicsCursorReset(String topicName) throws Exception {
         admin.namespaces().setRetention("prop-xyz/use/ns1", new RetentionPolicies(10, 10));
 
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
 
         topicName = "persistent://prop-xyz/use/ns1/" + topicName;
 
@@ -1443,7 +1443,7 @@ public void persistentTopicsCursorReset(String topicName) throws Exception {
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).acknowledgmentGroupTime(0, TimeUnit.SECONDS).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, 5, 0);
 
@@ -1453,7 +1453,7 @@ public void persistentTopicsCursorReset(String topicName) throws Exception {
 
         publishMessagesOnPersistentTopic(topicName, 5, 5);
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(topicName, "my-sub", 10);
         assertEquals(messages.size(), 10);
 
         for (int i = 0; i < 10; i++) {
@@ -1462,7 +1462,7 @@ public void persistentTopicsCursorReset(String topicName) throws Exception {
         }
         // messages should still be available due to retention
 
-        admin.persistentTopics().resetCursor(topicName, "my-sub", messageTimestamp);
+        admin.topics().resetCursor(topicName, "my-sub", messageTimestamp);
 
         int receivedAfterReset = 0;
 
@@ -1477,16 +1477,16 @@ public void persistentTopicsCursorReset(String topicName) throws Exception {
 
         consumer.close();
 
-        admin.persistentTopics().deleteSubscription(topicName, "my-sub");
+        admin.topics().deleteSubscription(topicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList());
-        admin.persistentTopics().delete(topicName);
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList());
+        admin.topics().delete(topicName);
     }
 
     @Test(dataProvider = "topicName")
     public void persistentTopicsCursorResetAfterReset(String topicName) throws Exception {
         admin.namespaces().setRetention("prop-xyz/use/ns1", new RetentionPolicies(10, 10));
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
 
         topicName = "persistent://prop-xyz/use/ns1/" + topicName;
 
@@ -1494,7 +1494,7 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).acknowledgmentGroupTime(0, TimeUnit.SECONDS).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, 5, 0);
 
@@ -1508,7 +1508,7 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
 
         publishMessagesOnPersistentTopic(topicName, 2, 8);
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(topicName, "my-sub", 10);
         assertEquals(messages.size(), 10);
         messages.forEach(message -> {
             LOG.info("Peeked message: {}", new String(message.getData()));
@@ -1519,7 +1519,7 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
             consumer.acknowledge(message);
         }
 
-        admin.persistentTopics().resetCursor(topicName, "my-sub", firstTimestamp);
+        admin.topics().resetCursor(topicName, "my-sub", firstTimestamp);
 
         int receivedAfterReset = 0;
 
@@ -1535,7 +1535,7 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
 
         // Reset at 2nd timestamp
         receivedAfterReset = 0;
-        admin.persistentTopics().resetCursor(topicName, "my-sub", secondTimestamp);
+        admin.topics().resetCursor(topicName, "my-sub", secondTimestamp);
 
         // Should received messages from 7-9
         for (int i = 7; i < 10; i++) {
@@ -1548,10 +1548,10 @@ public void persistentTopicsCursorResetAfterReset(String topicName) throws Excep
         assertEquals(receivedAfterReset, 3);
 
         consumer.close();
-        admin.persistentTopics().deleteSubscription(topicName, "my-sub");
+        admin.topics().deleteSubscription(topicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList());
-        admin.persistentTopics().delete(topicName);
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList());
+        admin.topics().delete(topicName);
     }
 
     @Test(dataProvider = "topicName")
@@ -1559,16 +1559,16 @@ public void partitionedTopicsCursorReset(String topicName) throws Exception {
         admin.namespaces().setRetention("prop-xyz/use/ns1", new RetentionPolicies(10, 10));
         topicName = "persistent://prop-xyz/use/ns1/" + topicName;
 
-        admin.persistentTopics().createPartitionedTopic(topicName, 4);
+        admin.topics().createPartitionedTopic(topicName, 4);
 
         // create consumer and subscription
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).acknowledgmentGroupTime(0, TimeUnit.SECONDS).subscribe();
 
-        List<String> topics = admin.persistentTopics().getList("prop-xyz/use/ns1");
+        List<String> topics = admin.topics().getList("prop-xyz/use/ns1");
         assertEquals(topics.size(), 4);
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, 5, 0);
         Thread.sleep(1);
@@ -1582,7 +1582,7 @@ public void partitionedTopicsCursorReset(String topicName) throws Exception {
         }
         // messages should still be available due to retention
 
-        admin.persistentTopics().resetCursor(topicName, "my-sub", timestamp);
+        admin.topics().resetCursor(topicName, "my-sub", timestamp);
 
         Set<String> expectedMessages = Sets.newHashSet();
         Set<String> receivedMessages = Sets.newHashSet();
@@ -1597,20 +1597,20 @@ public void partitionedTopicsCursorReset(String topicName) throws Exception {
         assertEquals(receivedMessages.size(), 0);
 
         consumer.close();
-        admin.persistentTopics().deleteSubscription(topicName, "my-sub");
-        admin.persistentTopics().deletePartitionedTopic(topicName);
+        admin.topics().deleteSubscription(topicName, "my-sub");
+        admin.topics().deletePartitionedTopic(topicName);
     }
 
     @Test
     public void persistentTopicsInvalidCursorReset() throws Exception {
         admin.namespaces().setRetention("prop-xyz/use/ns1", new RetentionPolicies(10, 10));
 
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
 
         String topicName = "persistent://prop-xyz/use/ns1/invalidcursorreset";
         // Force to create a topic
         publishMessagesOnPersistentTopic(topicName, 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList(topicName));
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"), Lists.newArrayList(topicName));
 
         // create consumer and subscription
         URL pulsarUrl = new URL("http://127.0.0.1" + ":" + BROKER_WEBSERVICE_PORT);
@@ -1619,11 +1619,11 @@ public void persistentTopicsInvalidCursorReset() throws Exception {
         Consumer<byte[]> consumer = client.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Exclusive).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, 10);
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(topicName, "my-sub", 10);
         assertEquals(messages.size(), 10);
 
         for (int i = 0; i < 10; i++) {
@@ -1632,21 +1632,21 @@ public void persistentTopicsInvalidCursorReset() throws Exception {
         }
         // use invalid timestamp
         try {
-            admin.persistentTopics().resetCursor(topicName, "my-sub", System.currentTimeMillis() - 190000);
+            admin.topics().resetCursor(topicName, "my-sub", System.currentTimeMillis() - 190000);
         } catch (Exception e) {
             // fail the test
             throw e;
         }
 
-        admin.persistentTopics().resetCursor(topicName, "my-sub", System.currentTimeMillis() + 90000);
+        admin.topics().resetCursor(topicName, "my-sub", System.currentTimeMillis() + 90000);
         consumer = client.newConsumer().topic(topicName).subscriptionName("my-sub").subscribe();
         consumer.close();
         client.close();
 
-        admin.persistentTopics().deleteSubscription(topicName, "my-sub");
+        admin.topics().deleteSubscription(topicName, "my-sub");
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList());
-        admin.persistentTopics().delete(topicName);
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList());
+        admin.topics().delete(topicName);
     }
 
     @Test
@@ -1686,7 +1686,7 @@ public void testPersistentTopicsExpireMessages() throws Exception {
 
         // Force to create a topic
         publishMessagesOnPersistentTopic("persistent://prop-xyz/use/ns1/ds2", 0);
-        assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"),
+        assertEquals(admin.topics().getList("prop-xyz/use/ns1"),
                 Lists.newArrayList("persistent://prop-xyz/use/ns1/ds2"));
 
         // create consumer and subscription
@@ -1699,28 +1699,28 @@ public void testPersistentTopicsExpireMessages() throws Exception {
         Consumer<byte[]> consumer2 = consumerBuilder.clone().subscriptionName("my-sub2").subscribe();
         Consumer<byte[]> consumer3 = consumerBuilder.clone().subscriptionName("my-sub3").subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions("persistent://prop-xyz/use/ns1/ds2").size(), 3);
+        assertEquals(admin.topics().getSubscriptions("persistent://prop-xyz/use/ns1/ds2").size(), 3);
 
         publishMessagesOnPersistentTopic("persistent://prop-xyz/use/ns1/ds2", 10);
 
-        PersistentTopicStats topicStats = admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1/ds2");
+        TopicStats topicStats = admin.topics().getStats("persistent://prop-xyz/use/ns1/ds2");
         assertEquals(topicStats.subscriptions.get("my-sub1").msgBacklog, 10);
         assertEquals(topicStats.subscriptions.get("my-sub2").msgBacklog, 10);
         assertEquals(topicStats.subscriptions.get("my-sub3").msgBacklog, 10);
 
         Thread.sleep(1000); // wait for 1 seconds to expire message
-        admin.persistentTopics().expireMessages("persistent://prop-xyz/use/ns1/ds2", "my-sub1", 1);
+        admin.topics().expireMessages("persistent://prop-xyz/use/ns1/ds2", "my-sub1", 1);
         Thread.sleep(1000); // wait for 1 seconds to execute expire message as it is async
 
-        topicStats = admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1/ds2");
+        topicStats = admin.topics().getStats("persistent://prop-xyz/use/ns1/ds2");
         assertEquals(topicStats.subscriptions.get("my-sub1").msgBacklog, 0);
         assertEquals(topicStats.subscriptions.get("my-sub2").msgBacklog, 10);
         assertEquals(topicStats.subscriptions.get("my-sub3").msgBacklog, 10);
 
-        admin.persistentTopics().expireMessagesForAllSubscriptions("persistent://prop-xyz/use/ns1/ds2", 1);
+        admin.topics().expireMessagesForAllSubscriptions("persistent://prop-xyz/use/ns1/ds2", 1);
         Thread.sleep(1000); // wait for 1 seconds to execute expire message as it is async
 
-        topicStats = admin.persistentTopics().getStats("persistent://prop-xyz/use/ns1/ds2");
+        topicStats = admin.topics().getStats("persistent://prop-xyz/use/ns1/ds2");
         assertEquals(topicStats.subscriptions.get("my-sub1").msgBacklog, 0);
         assertEquals(topicStats.subscriptions.get("my-sub2").msgBacklog, 0);
         assertEquals(topicStats.subscriptions.get("my-sub3").msgBacklog, 0);
@@ -1739,7 +1739,7 @@ public void testPersistentTopicsExpireMessages() throws Exception {
     @Test
     public void testPersistentTopicExpireMessageOnParitionTopic() throws Exception {
 
-        admin.persistentTopics().createPartitionedTopic("persistent://prop-xyz/use/ns1/ds1", 4);
+        admin.topics().createPartitionedTopic("persistent://prop-xyz/use/ns1/ds1", 4);
 
         // create consumer and subscription
         URL pulsarUrl = new URL("http://127.0.0.1" + ":" + BROKER_WEBSERVICE_PORT);
@@ -1758,22 +1758,22 @@ public void testPersistentTopicExpireMessageOnParitionTopic() throws Exception {
             producer.send(message.getBytes());
         }
 
-        PartitionedTopicStats topicStats = admin.persistentTopics()
+        PartitionedTopicStats topicStats = admin.topics()
                 .getPartitionedStats("persistent://prop-xyz/use/ns1/ds1", true);
         assertEquals(topicStats.subscriptions.get("my-sub").msgBacklog, 10);
 
-        PersistentTopicStats partitionStatsPartition0 = topicStats.partitions
+        TopicStats partitionStatsPartition0 = topicStats.partitions
                 .get("persistent://prop-xyz/use/ns1/ds1-partition-0");
-        PersistentTopicStats partitionStatsPartition1 = topicStats.partitions
+        TopicStats partitionStatsPartition1 = topicStats.partitions
                 .get("persistent://prop-xyz/use/ns1/ds1-partition-1");
         assertEquals(partitionStatsPartition0.subscriptions.get("my-sub").msgBacklog, 3, 1);
         assertEquals(partitionStatsPartition1.subscriptions.get("my-sub").msgBacklog, 3, 1);
 
         Thread.sleep(1000);
-        admin.persistentTopics().expireMessagesForAllSubscriptions("persistent://prop-xyz/use/ns1/ds1", 1);
+        admin.topics().expireMessagesForAllSubscriptions("persistent://prop-xyz/use/ns1/ds1", 1);
         Thread.sleep(1000);
 
-        topicStats = admin.persistentTopics().getPartitionedStats("persistent://prop-xyz/use/ns1/ds1", true);
+        topicStats = admin.topics().getPartitionedStats("persistent://prop-xyz/use/ns1/ds1", true);
         partitionStatsPartition0 = topicStats.partitions.get("persistent://prop-xyz/use/ns1/ds1-partition-0");
         partitionStatsPartition1 = topicStats.partitions.get("persistent://prop-xyz/use/ns1/ds1-partition-1");
         assertEquals(partitionStatsPartition0.subscriptions.get("my-sub").msgBacklog, 0);
@@ -1800,14 +1800,14 @@ public void testPulsarAdminForUriAndUrlEncoding(String topicName) throws Excepti
         final String urlEncodedTopic = Codec.encode(topicName);
         final String uriEncodedTopic = urlEncodedTopic.replaceAll("\\+", "%20");
         final int numOfPartitions = 4;
-        admin.persistentTopics().createPartitionedTopic(topic1, numOfPartitions);
+        admin.topics().createPartitionedTopic(topic1, numOfPartitions);
         // Create a consumer to get stats on this topic
         pulsarClient.newConsumer().topic(topic1).subscriptionName("my-subscriber-name").subscribe();
 
-        PersistentTopicsImpl persistent = (PersistentTopicsImpl) admin.persistentTopics();
-        Field field = PersistentTopicsImpl.class.getDeclaredField("adminPersistentTopics");
+        TopicsImpl persistent = (TopicsImpl) admin.topics();
+        Field field = TopicsImpl.class.getDeclaredField("adminTopics");
         field.setAccessible(true);
-        WebTarget persistentTopics = (WebTarget) field.get(persistent);
+        WebTarget persistentTopics = ((WebTarget) field.get(persistent)).path("persistent");
 
         // (1) Get PartitionedMetadata : with Url and Uri encoding
         final CompletableFuture<PartitionedTopicMetadata> urlEncodedPartitionedMetadata = new CompletableFuture<>();
@@ -1821,7 +1821,7 @@ public void completed(PartitionedTopicMetadata response) {
 
                     @Override
                     public void failed(Throwable e) {
-                        Assert.fail(e.getMessage());
+                        urlEncodedPartitionedMetadata.completeExceptionally(e);
                     }
                 });
         final CompletableFuture<PartitionedTopicMetadata> uriEncodedPartitionedMetadata = new CompletableFuture<>();
@@ -1858,12 +1858,12 @@ public void failed(Throwable e) {
         assertEquals(urlEncodedLookupData.getBrokerUrl(), uriEncodedLookupData.getBrokerUrl());
 
         // (3) Get Topic Stats
-        final CompletableFuture<PersistentTopicStats> urlStats = new CompletableFuture<>();
+        final CompletableFuture<TopicStats> urlStats = new CompletableFuture<>();
         // (a) Url encoding
         persistent.asyncGetRequest(persistentTopics.path(ns1).path(urlEncodedTopic + "-partition-1").path("stats"),
-                new InvocationCallback<PersistentTopicStats>() {
+                new InvocationCallback<TopicStats>() {
                     @Override
-                    public void completed(PersistentTopicStats response) {
+                    public void completed(TopicStats response) {
                         urlStats.complete(response);
                     }
 
@@ -1873,11 +1873,11 @@ public void failed(Throwable e) {
                     }
                 });
         // (b) Uri encoding
-        final CompletableFuture<PersistentTopicStats> uriStats = new CompletableFuture<>();
+        final CompletableFuture<TopicStats> uriStats = new CompletableFuture<>();
         persistent.asyncGetRequest(persistentTopics.path(ns1).path(uriEncodedTopic + "-partition-1").path("stats"),
-                new InvocationCallback<PersistentTopicStats>() {
+                new InvocationCallback<TopicStats>() {
                     @Override
-                    public void completed(PersistentTopicStats response) {
+                    public void completed(TopicStats response) {
                         uriStats.complete(response);
                     }
 
@@ -1946,12 +1946,12 @@ public void testTriggerCompaction() throws Exception {
         CompletableFuture<Long> promise = new CompletableFuture<Long>();
         Compactor compactor = pulsar.getCompactor();
         doReturn(promise).when(compactor).compact(topicName);
-        admin.persistentTopics().triggerCompaction(topicName);
+        admin.topics().triggerCompaction(topicName);
 
         // verify compact called once
         verify(compactor).compact(topicName);
         try {
-            admin.persistentTopics().triggerCompaction(topicName);
+            admin.topics().triggerCompaction(topicName);
 
             fail("Shouldn't be able to run while already running");
         } catch (ConflictException e) {
@@ -1962,7 +1962,7 @@ public void testTriggerCompaction() throws Exception {
 
         // complete first compaction, and trigger again
         promise.complete(1L);
-        admin.persistentTopics().triggerCompaction(topicName);
+        admin.topics().triggerCompaction(topicName);
 
         // verify compact was called again
         verify(compactor, times(2)).compact(topicName);
@@ -1976,31 +1976,31 @@ public void testCompactionStatus() throws Exception {
         pulsarClient.newProducer().topic(topicName).create().close();
         assertNotNull(pulsar.getBrokerService().getTopicReference(topicName));
 
-        assertEquals(admin.persistentTopics().compactionStatus(topicName).status,
+        assertEquals(admin.topics().compactionStatus(topicName).status,
                      CompactionStatus.Status.NOT_RUN);
 
         // mock actual compaction, we don't need to really run it
         CompletableFuture<Long> promise = new CompletableFuture<Long>();
         Compactor compactor = pulsar.getCompactor();
         doReturn(promise).when(compactor).compact(topicName);
-        admin.persistentTopics().triggerCompaction(topicName);
+        admin.topics().triggerCompaction(topicName);
 
-        assertEquals(admin.persistentTopics().compactionStatus(topicName).status,
+        assertEquals(admin.topics().compactionStatus(topicName).status,
                      CompactionStatus.Status.RUNNING);
 
         promise.complete(1L);
 
-        assertEquals(admin.persistentTopics().compactionStatus(topicName).status,
+        assertEquals(admin.topics().compactionStatus(topicName).status,
                      CompactionStatus.Status.SUCCESS);
 
         CompletableFuture<Long> errorPromise = new CompletableFuture<Long>();
         doReturn(errorPromise).when(compactor).compact(topicName);
-        admin.persistentTopics().triggerCompaction(topicName);
+        admin.topics().triggerCompaction(topicName);
         errorPromise.completeExceptionally(new Exception("Failed at something"));
 
-        assertEquals(admin.persistentTopics().compactionStatus(topicName).status,
+        assertEquals(admin.topics().compactionStatus(topicName).status,
                      CompactionStatus.Status.ERROR);
-        assertTrue(admin.persistentTopics().compactionStatus(topicName)
+        assertTrue(admin.topics().compactionStatus(topicName)
                    .lastError.contains("Failed at something"));
     }
 }
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/v1/V1_AdminApiTest2.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/v1/V1_AdminApiTest2.java
index 355114145b..39cbf328e7 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/v1/V1_AdminApiTest2.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/v1/V1_AdminApiTest2.java
@@ -64,7 +64,7 @@
 import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
 import org.apache.pulsar.common.policies.data.PersistencePolicies;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.TenantInfo;
 import org.apache.pulsar.common.policies.data.RetentionPolicies;
 import org.apache.pulsar.common.policies.data.SubscriptionStats;
@@ -139,27 +139,27 @@ public void testIncrementPartitionsOfTopic() throws Exception {
 
         URL pulsarUrl = new URL("http://127.0.0.1" + ":" + BROKER_WEBSERVICE_PORT);
 
-        admin.persistentTopics().createPartitionedTopic(partitionedTopicName, startPartitions);
+        admin.topics().createPartitionedTopic(partitionedTopicName, startPartitions);
         // validate partition topic is created
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions,
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions,
                 startPartitions);
 
         // create consumer and subscriptions : check subscriptions
         PulsarClient client = PulsarClient.builder().serviceUrl(pulsarUrl.toString()).build();
         Consumer<byte[]> consumer1 = client.newConsumer().topic(partitionedTopicName).subscriptionName(subName1)
                 .subscriptionType(SubscriptionType.Shared).subscribe();
-        assertEquals(admin.persistentTopics().getSubscriptions(partitionedTopicName), Lists.newArrayList(subName1));
+        assertEquals(admin.topics().getSubscriptions(partitionedTopicName), Lists.newArrayList(subName1));
         Consumer<byte[]> consumer2 = client.newConsumer().topic(partitionedTopicName).subscriptionName(subName2)
                 .subscriptionType(SubscriptionType.Shared).subscribe();
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getSubscriptions(partitionedTopicName)),
+        assertEquals(Sets.newHashSet(admin.topics().getSubscriptions(partitionedTopicName)),
                 Sets.newHashSet(subName1, subName2));
 
         // (1) update partitions
-        admin.persistentTopics().updatePartitionedTopic(partitionedTopicName, newPartitions);
+        admin.topics().updatePartitionedTopic(partitionedTopicName, newPartitions);
         // invalidate global-cache to make sure that mock-zk-cache reds fresh data
         pulsar.getGlobalZkCache().invalidateAll();
         // verify new partitions have been created
-        assertEquals(admin.persistentTopics().getPartitionedTopicMetadata(partitionedTopicName).partitions,
+        assertEquals(admin.topics().getPartitionedTopicMetadata(partitionedTopicName).partitions,
                 newPartitions);
         // (2) No Msg loss: verify new partitions have the same existing subscription names
         final String newPartitionTopicName = TopicName.get(partitionedTopicName).getPartition(startPartitions + 1)
@@ -183,13 +183,13 @@ public void testIncrementPartitionsOfTopic() throws Exception {
                 .subscriptionType(SubscriptionType.Shared).subscribe();
         // sometime: mockZk fails to refresh ml-cache: so, invalidate the cache to get fresh data
         pulsar.getLocalZkCacheService().managedLedgerListCache().clearTree();
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getSubscriptions(newPartitionTopicName)),
+        assertEquals(Sets.newHashSet(admin.topics().getSubscriptions(newPartitionTopicName)),
                 Sets.newHashSet(subName1, subName2));
 
-        assertEquals(Sets.newHashSet(admin.persistentTopics().getList("prop-xyz/use/ns1")).size(), newPartitions);
+        assertEquals(Sets.newHashSet(admin.topics().getList("prop-xyz/use/ns1")).size(), newPartitions);
 
         // test cumulative stats for partitioned topic
-        PartitionedTopicStats topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, false);
+        PartitionedTopicStats topicStats = admin.topics().getPartitionedStats(partitionedTopicName, false);
         assertEquals(topicStats.subscriptions.keySet(), Sets.newTreeSet(Lists.newArrayList(subName1, subName2)));
         assertEquals(topicStats.subscriptions.get(subName2).consumers.size(), 1);
         assertEquals(topicStats.subscriptions.get(subName2).msgBacklog, totalMessages);
@@ -197,7 +197,7 @@ public void testIncrementPartitionsOfTopic() throws Exception {
         assertEquals(topicStats.partitions, Maps.newHashMap());
 
         // (5) verify: each partition should have backlog
-        topicStats = admin.persistentTopics().getPartitionedStats(partitionedTopicName, true);
+        topicStats = admin.topics().getPartitionedStats(partitionedTopicName, true);
         assertEquals(topicStats.metadata.partitions, newPartitions);
         Set<String> partitionSet = Sets.newHashSet();
         for (int i = 0; i < newPartitions; i++) {
@@ -205,7 +205,7 @@ public void testIncrementPartitionsOfTopic() throws Exception {
         }
         assertEquals(topicStats.partitions.keySet(), partitionSet);
         for (int i = 0; i < newPartitions; i++) {
-            PersistentTopicStats partitionStats = topicStats.partitions
+            TopicStats partitionStats = topicStats.partitions
                     .get(TopicName.get(partitionedTopicName).getPartition(i).toString());
             assertEquals(partitionStats.publishers.size(), 1);
             assertEquals(partitionStats.subscriptions.get(subName2).consumers.size(), 1);
@@ -402,7 +402,7 @@ public void testUnloadTopic(final String topicType) throws Exception {
 
     private void unloadTopic(String topicName, boolean isPersistentTopic) throws Exception {
         if (isPersistentTopic) {
-            admin.persistentTopics().unload(topicName);
+            admin.topics().unload(topicName);
         } else {
             admin.nonPersistentTopics().unload(topicName);
         }
@@ -433,11 +433,11 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Shared).subscribe();
 
-        assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
+        assertEquals(admin.topics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
 
         publishMessagesOnPersistentTopic(topicName, totalProducedMessages, 0);
 
-        List<Message<byte[]>> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
+        List<Message<byte[]>> messages = admin.topics().peekMessages(topicName, "my-sub", 10);
         assertEquals(messages.size(), 10);
 
         Message<byte[]> message = null;
@@ -457,7 +457,7 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
         // messages should still be available due to retention
         MessageIdImpl messageId = new MessageIdImpl(resetMessageId.getLedgerId(), resetMessageId.getEntryId(), -1);
         // reset position at resetMessageId
-        admin.persistentTopics().resetCursor(topicName, "my-sub", messageId);
+        admin.topics().resetCursor(topicName, "my-sub", messageId);
 
         consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName("my-sub")
                 .subscriptionType(SubscriptionType.Shared).subscribe();
@@ -478,7 +478,7 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
 
         // invalid topic name
         try {
-            admin.persistentTopics().resetCursor(topicName + "invalid", "my-sub", messageId);
+            admin.topics().resetCursor(topicName + "invalid", "my-sub", messageId);
             fail("It should have failed due to invalid topic name");
         } catch (PulsarAdminException.NotFoundException e) {
             // Ok
@@ -486,7 +486,7 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
 
         // invalid cursor name
         try {
-            admin.persistentTopics().resetCursor(topicName, "invalid-sub", messageId);
+            admin.topics().resetCursor(topicName, "invalid-sub", messageId);
             fail("It should have failed due to invalid subscription name");
         } catch (PulsarAdminException.NotFoundException e) {
             // Ok
@@ -495,7 +495,7 @@ public void testResetCursorOnPosition(String namespaceName) throws Exception {
         // invalid position
         try {
             messageId = new MessageIdImpl(0, 0, -1);
-            admin.persistentTopics().resetCursor(topicName, "my-sub", messageId);
+            admin.topics().resetCursor(topicName, "my-sub", messageId);
             fail("It should have failed due to invalid subscription name");
         } catch (PulsarAdminException.PreconditionFailedException e) {
             // Ok
@@ -761,9 +761,9 @@ public void testPublishConsumerStats() throws Exception {
             .create();
 
         retryStrategically((test) -> {
-            PersistentTopicStats stats;
+            TopicStats stats;
             try {
-                stats = admin.persistentTopics().getStats(topic);
+                stats = admin.topics().getStats(topic);
                 return stats.publishers.size() > 0 && stats.subscriptions.get(subscriberName) != null
                         && stats.subscriptions.get(subscriberName).consumers.size() > 0;
             } catch (PulsarAdminException e) {
@@ -771,7 +771,7 @@ public void testPublishConsumerStats() throws Exception {
             }
         }, 5, 200);
 
-        PersistentTopicStats topicStats = admin.persistentTopics().getStats(topic);
+        TopicStats topicStats = admin.topics().getStats(topic);
         assertEquals(topicStats.publishers.size(), 1);
         assertNotNull(topicStats.publishers.get(0).getAddress());
         assertNotNull(topicStats.publishers.get(0).getClientVersion());
@@ -806,7 +806,7 @@ public void testTenantNameWithUnderscore() throws Exception {
             .messageRoutingMode(MessageRoutingMode.SinglePartition)
             .create();
 
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topic);
+        TopicStats stats = admin.topics().getStats(topic);
         assertEquals(stats.publishers.size(), 1);
         producer.close();
     }
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/auth/AuthorizationTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/auth/AuthorizationTest.java
index 526d6e744e..b6b7f246f7 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/auth/AuthorizationTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/auth/AuthorizationTest.java
@@ -79,7 +79,7 @@ void simple() throws Exception {
         assertEquals(auth.canLookup(TopicName.get("persistent://p1/c1/ns1/ds1"), "my-role", null), true);
         assertEquals(auth.canProduce(TopicName.get("persistent://p1/c1/ns1/ds1"), "my-role", null), true);
 
-        admin.persistentTopics().grantPermission("persistent://p1/c1/ns1/ds2", "other-role",
+        admin.topics().grantPermission("persistent://p1/c1/ns1/ds2", "other-role",
                 EnumSet.of(AuthAction.consume));
         waitForChange();
 
@@ -150,7 +150,7 @@ void simple() throws Exception {
         assertEquals(auth.canLookup(TopicName.get("persistent://p1/c1/ns1/ds2"), "my.role.1", null), false);
         assertEquals(auth.canLookup(TopicName.get("persistent://p1/c1/ns1/ds2"), "my.role.2", null), false);
 
-        admin.persistentTopics().grantPermission("persistent://p1/c1/ns1/ds1", "my.*",
+        admin.topics().grantPermission("persistent://p1/c1/ns1/ds1", "my.*",
                 EnumSet.of(AuthAction.produce));
         waitForChange();
 
@@ -173,7 +173,7 @@ void simple() throws Exception {
         assertEquals(auth.canLookup(TopicName.get("persistent://p1/c1/ns1/ds2"), "1.role.my", null), false);
         assertEquals(auth.canLookup(TopicName.get("persistent://p1/c1/ns1/ds2"), "2.role.my", null), false);
 
-        admin.persistentTopics().grantPermission("persistent://p1/c1/ns1/ds1", "*.my",
+        admin.topics().grantPermission("persistent://p1/c1/ns1/ds1", "*.my",
                 EnumSet.of(AuthAction.consume));
         waitForChange();
 
@@ -186,8 +186,8 @@ void simple() throws Exception {
         assertEquals(auth.canLookup(TopicName.get("persistent://p1/c1/ns1/ds2"), "1.role.my", null), false);
         assertEquals(auth.canLookup(TopicName.get("persistent://p1/c1/ns1/ds2"), "2.role.my", null), false);
 
-        admin.persistentTopics().revokePermissions("persistent://p1/c1/ns1/ds1", "my.*");
-        admin.persistentTopics().revokePermissions("persistent://p1/c1/ns1/ds1", "*.my");
+        admin.topics().revokePermissions("persistent://p1/c1/ns1/ds1", "my.*");
+        admin.topics().revokePermissions("persistent://p1/c1/ns1/ds1", "*.my");
 
         // tests for subscription auth mode
         admin.namespaces().grantPermissionOnNamespace("p1/c1/ns1", "*", EnumSet.of(AuthAction.consume));
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/BacklogQuotaManagerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/BacklogQuotaManagerTest.java
index 68235df0d7..00265594a8 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/BacklogQuotaManagerTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/BacklogQuotaManagerTest.java
@@ -37,7 +37,7 @@
 import org.apache.pulsar.client.api.PulsarClientException;
 import org.apache.pulsar.common.policies.data.BacklogQuota;
 import org.apache.pulsar.common.policies.data.ClusterData;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.TenantInfo;
 import org.apache.pulsar.zookeeper.LocalBookkeeperEnsemble;
 import org.slf4j.Logger;
@@ -149,7 +149,7 @@ public void testConsumerBacklogEviction() throws Exception {
         Thread.sleep((TIME_TO_CHECK_BACKLOG_QUOTA + 1) * 1000);
         rolloverStats();
 
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topic1);
+        TopicStats stats = admin.topics().getStats(topic1);
         Assert.assertTrue(stats.storageSize < 10 * 1024, "Storage size is [" + stats.storageSize + "]");
         client.close();
     }
@@ -180,7 +180,7 @@ public void testConsumerBacklogEvictionWithAck() throws Exception {
         Thread.sleep((TIME_TO_CHECK_BACKLOG_QUOTA + 1) * 1000);
         rolloverStats();
 
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topic1);
+        TopicStats stats = admin.topics().getStats(topic1);
         Assert.assertTrue(stats.storageSize <= 10 * 1024, "Storage size is [" + stats.storageSize + "]");
         client.close();
     }
@@ -251,7 +251,7 @@ public void run() {
         Thread.sleep((TIME_TO_CHECK_BACKLOG_QUOTA + 1) * 1000);
         rolloverStats();
 
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topic1);
+        TopicStats stats = admin.topics().getStats(topic1);
         Assert.assertTrue(stats.storageSize <= 10 * 1024, "Storage size is [" + stats.storageSize + "]");
         client.close();
         client2.close();
@@ -423,7 +423,7 @@ public void run() {
         Thread.sleep((TIME_TO_CHECK_BACKLOG_QUOTA + 1) * 1000);
         rolloverStats();
 
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topic1);
+        TopicStats stats = admin.topics().getStats(topic1);
         Assert.assertTrue(stats.storageSize <= 15 * 1024, "Storage size is [" + stats.storageSize + "]");
         client.close();
         client2.close();
@@ -462,7 +462,7 @@ public void testAheadProducerOnHold() throws Exception {
 
         Thread.sleep((TIME_TO_CHECK_BACKLOG_QUOTA + 1) * 1000);
         rolloverStats();
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topic1);
+        TopicStats stats = admin.topics().getStats(topic1);
         Assert.assertEquals(stats.publishers.size(), 0,
                 "Number of producers on topic " + topic1 + " are [" + stats.publishers.size() + "]");
         client.close();
@@ -573,7 +573,7 @@ public void testProducerExceptionAndThenUnblock() throws Exception {
         Assert.assertTrue(gotException, "backlog exceeded exception did not occur");
         // now remove backlog and ensure that producer is unblockedrolloverStats();
 
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topic1);
+        TopicStats stats = admin.topics().getStats(topic1);
         int backlog = (int) stats.subscriptions.get(subName1).msgBacklog;
 
         for (int i = 0; i < backlog; i++) {
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/BrokerServiceTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/BrokerServiceTest.java
index 31773bbd67..9fa40d41a4 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/BrokerServiceTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/BrokerServiceTest.java
@@ -65,7 +65,7 @@
 import org.apache.pulsar.common.naming.TopicName;
 import org.apache.pulsar.common.policies.data.BundlesData;
 import org.apache.pulsar.common.policies.data.LocalPolicies;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.SubscriptionStats;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -138,7 +138,7 @@ public void testBrokerServicePersistentTopicStats() throws Exception {
         final String topicName = "persistent://prop/ns-abc/successTopic";
         final String subName = "successSub";
 
-        PersistentTopicStats stats;
+        TopicStats stats;
         SubscriptionStats subStats;
 
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName(subName)
@@ -215,7 +215,7 @@ public void testBrokerServicePersistentRedeliverTopicStats() throws Exception {
         final String topicName = "persistent://prop/ns-abc/successSharedTopic";
         final String subName = "successSharedSub";
 
-        PersistentTopicStats stats;
+        TopicStats stats;
         SubscriptionStats subStats;
 
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName).subscriptionName(subName)
@@ -395,7 +395,7 @@ public void testBrokerServiceNamespaceStats() throws Exception {
         for (String ns : nsList) {
             List<String> topics = admin.namespaces().getTopics(ns);
             for (String dest : topics) {
-                admin.persistentTopics().delete(dest);
+                admin.topics().delete(dest);
             }
             admin.namespaces().deleteNamespace(ns);
         }
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PeerReplicatorTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PeerReplicatorTest.java
index 5ccf65fd34..b44cbf5174 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PeerReplicatorTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PeerReplicatorTest.java
@@ -31,7 +31,7 @@
 import org.apache.pulsar.client.api.Producer;
 import org.apache.pulsar.client.api.PulsarClient;
 import org.apache.pulsar.client.api.PulsarClientException;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -121,10 +121,10 @@ public void testPeerClusterTopicLookup(String protocol) throws Exception {
         assertNotNull(topic);
         pulsar1.getBrokerService().updateRates();
         // get stats for topic1 using cluster-r3's admin3
-        PersistentTopicStats stats = admin1.persistentTopics().getStats(topic1);
+        TopicStats stats = admin1.topics().getStats(topic1);
         assertNotNull(stats);
         assertEquals(stats.publishers.size(), 1);
-        stats = admin3.persistentTopics().getStats(topic1);
+        stats = admin3.topics().getStats(topic1);
         assertNotNull(stats);
         assertEquals(stats.publishers.size(), 1);
         producer.close();
@@ -136,10 +136,10 @@ public void testPeerClusterTopicLookup(String protocol) throws Exception {
         assertNotNull(topic);
         pulsar2.getBrokerService().updateRates();
         // get stats for topic1 using cluster-r3's admin3
-        stats = admin3.persistentTopics().getStats(topic2);
+        stats = admin3.topics().getStats(topic2);
         assertNotNull(stats);
         assertEquals(stats.publishers.size(), 1);
-        stats = admin3.persistentTopics().getStats(topic2);
+        stats = admin3.topics().getStats(topic2);
         assertNotNull(stats);
         assertEquals(stats.publishers.size(), 1);
         producer.close();
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentFailoverE2ETest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentFailoverE2ETest.java
index e702f6e039..6c61734c2f 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentFailoverE2ETest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentFailoverE2ETest.java
@@ -315,7 +315,7 @@ public void testSimpleConsumerEventsWithoutPartition() throws Exception {
         producer.close();
         consumer3.close();
 
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
     }
 
     @Test
@@ -327,7 +327,7 @@ public void testSimpleConsumerEventsWithPartition() throws Exception {
         final String subName = "sub1";
         final int numMsgs = 100;
         Set<String> uniqueMessages = new HashSet<>();
-        admin.persistentTopics().createPartitionedTopic(topicName, numPartitions);
+        admin.topics().createPartitionedTopic(topicName, numPartitions);
 
         ConsumerBuilder<byte[]> consumerBuilder = pulsarClient.newConsumer().topic(topicName).subscriptionName(subName)
                 .subscriptionType(SubscriptionType.Failover);
@@ -513,7 +513,7 @@ public void testSimpleConsumerEventsWithPartition() throws Exception {
         consumer2.close();
         consumer3.unsubscribe();
 
-        admin.persistentTopics().deletePartitionedTopic(topicName);
+        admin.topics().deletePartitionedTopic(topicName);
     }
 
     @Test
@@ -585,7 +585,7 @@ public void testActiveConsumerFailoverWithDelay() throws Exception {
 
         subscribeFuture1.get().close();
         subscribeFuture2.get().unsubscribe();
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
         resetConfig();
         restartBroker();
     }
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentQueueE2ETest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentQueueE2ETest.java
index fce508bd83..c41a07a2cb 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentQueueE2ETest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentQueueE2ETest.java
@@ -46,7 +46,7 @@
 import org.apache.pulsar.client.impl.ConsumerImpl;
 import org.apache.pulsar.common.api.proto.PulsarApi.CommandSubscribe.SubType;
 import org.apache.pulsar.common.policies.data.ConsumerStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.SubscriptionStats;
 import org.apache.pulsar.common.util.FutureUtil;
 import org.eclipse.jetty.util.BlockingArrayQueue;
@@ -172,7 +172,7 @@ public void testSimpleConsumerEvents() throws Exception {
         producer.close();
         consumer2.close();
 
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
     }
 
     @Test
@@ -220,7 +220,7 @@ public void testReplayOnConsumerDisconnect() throws Exception {
         assertTrue(CollectionUtils.subtract(messagesProduced, messagesConsumed).isEmpty());
 
         consumer1.close();
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
     }
 
     @Test
@@ -279,7 +279,7 @@ public void testConsumersWithDifferentPermits() throws Exception {
 
         consumer1.close();
         consumer2.close();
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
     }
 
     // this test is good to have to see the distribution, but every now and then it gets slightly different than the
@@ -352,7 +352,7 @@ public void testRoundRobinBatchDistribution() throws Exception {
         consumer1.close();
         consumer2.close();
         consumer3.close();
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
     }
 
     @Test(timeOut = 300000)
@@ -537,7 +537,7 @@ public void testUnackedCountWithRedeliveries() throws Exception {
             consumer1.acknowledge(msgId);
         }
 
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topicName);
+        TopicStats stats = admin.topics().getStats(topicName);
 
         // Unacked messages count should be 0 for both consumers at this point
         SubscriptionStats subStats = stats.subscriptions.get(subName);
@@ -550,6 +550,6 @@ public void testUnackedCountWithRedeliveries() throws Exception {
         producer.close();
         consumer1.close();
         consumer2.close();
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
     }
 }
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentTopicE2ETest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentTopicE2ETest.java
index 8da33cd3d4..3c24451a61 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentTopicE2ETest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/PersistentTopicE2ETest.java
@@ -530,9 +530,9 @@ public void testTopicDeleteWithDisconnectedSubscription() throws Exception {
         assertFalse(subRef.getDispatcher().isConsumerConnected());
 
         // 3. delete topic
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
         try {
-            admin.persistentTopics().getStats(topicName);
+            admin.topics().getStats(topicName);
         } catch (PulsarAdminException e) {
             // ok
         }
@@ -596,7 +596,7 @@ public void testGC() throws Exception {
         assertTrue(pulsar.getBrokerService().getTopicReference(topicName).isPresent());
 
         // 4. Topic can be GCed after unsubscribe
-        admin.persistentTopics().deleteSubscription(topicName, subName);
+        admin.topics().deleteSubscription(topicName, subName);
 
         runGC();
         assertFalse(pulsar.getBrokerService().getTopicReference(topicName).isPresent());
@@ -640,7 +640,7 @@ public void testGcAndRetentionPolicy() throws Exception {
         assertNotNull(pulsar.getBrokerService().getTopicReference(topicName));
 
         // 4. Topic can be GCed after unsubscribe
-        admin.persistentTopics().deleteSubscription(topicName, subName);
+        admin.topics().deleteSubscription(topicName, subName);
 
         runGC();
         assertFalse(pulsar.getBrokerService().getTopicReference(topicName).isPresent());
@@ -683,7 +683,7 @@ public void testInfiniteRetentionPolicy() throws Exception {
         assertNotNull(pulsar.getBrokerService().getTopicReference(topicName));
 
         // 4. Topic can be GCed after unsubscribe
-        admin.persistentTopics().deleteSubscription(topicName, subName);
+        admin.topics().deleteSubscription(topicName, subName);
 
         runGC();
         assertFalse(pulsar.getBrokerService().getTopicReference(topicName).isPresent());
@@ -732,8 +732,8 @@ public void testMessageExpiry() throws Exception {
         // clean-up
         producer.close();
         consumer.close();
-        admin.persistentTopics().deleteSubscription(topicName, subName);
-        admin.persistentTopics().delete(topicName);
+        admin.topics().deleteSubscription(topicName, subName);
+        admin.topics().delete(topicName);
         admin.namespaces().deleteNamespace(namespaceName);
     }
 
@@ -852,7 +852,7 @@ public void testSubscriptionTypeTransitions() throws Exception {
         }
 
         consumer1.close();
-        admin.persistentTopics().delete(topicName);
+        admin.topics().delete(topicName);
     }
 
     @Test
@@ -1061,7 +1061,7 @@ public void testDeleteTopics() throws Exception {
         assertNotNull(bundleStats);
 
         producer1.close();
-        admin.persistentTopics().delete("persistent://prop/ns-abc/topic-1");
+        admin.topics().delete("persistent://prop/ns-abc/topic-1");
 
         brokerService.updateRates();
 
@@ -1072,7 +1072,7 @@ public void testDeleteTopics() throws Exception {
 
         // // Delete 2nd topic as well
         // producer2.close();
-        // admin.persistentTopics().delete("persistent://prop/ns-abc/topic-2");
+        // admin.topics().delete("persistent://prop/ns-abc/topic-2");
         //
         // brokerService.updateRates();
         //
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ReplicatorTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ReplicatorTest.java
index 8d2804f5f9..b5413c57ff 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ReplicatorTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ReplicatorTest.java
@@ -572,7 +572,7 @@ public Void call() throws Exception {
                 fail(String.format("replication test failed with %s exception", e.getMessage()));
             }
         }
-        admin1.persistentTopics().resetCursor(testDests.first(), "sub-id", System.currentTimeMillis());
+        admin1.topics().resetCursor(testDests.first(), "sub-id", System.currentTimeMillis());
     }
 
     @Test(enabled = true, timeOut = 30000)
@@ -898,8 +898,8 @@ public void testReplicatorOnPartitionedTopic(boolean isPartitionedTopic) throws
         admin1.namespaces().setNamespaceReplicationClusters(namespace, Sets.newHashSet("r1", "r2", "r3"));
 
         if (isPartitionedTopic) {
-            admin1.persistentTopics().createPartitionedTopic(persistentTopicName, 5);
-            admin1.nonPersistentTopics().createPartitionedTopic(nonPersistentTopicName, 5);
+            admin1.topics().createPartitionedTopic(persistentTopicName, 5);
+            admin1.topics().createPartitionedTopic(nonPersistentTopicName, 5);
         }
 
         // load namespace with dummy topic on ns
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ResendRequestTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ResendRequestTest.java
index 0acafadd9e..eecf0f2d71 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ResendRequestTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ResendRequestTest.java
@@ -421,7 +421,7 @@ public void testExclusiveSingleAckedPartitionedTopic() throws Exception {
         final int totalMessages = 10;
         final int numberOfPartitions = 4;
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName, numberOfPartitions);
+        admin.topics().createPartitionedTopic(topicName, numberOfPartitions);
         // Special step to create partitioned topic
 
         // 1. producer connect
@@ -476,7 +476,7 @@ public void testSharedSingleAckedPartitionedTopic() throws Exception {
         final int totalMessages = 10;
         final int numberOfPartitions = 3;
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName, numberOfPartitions);
+        admin.topics().createPartitionedTopic(topicName, numberOfPartitions);
         Random rn = new Random();
         // Special step to create partitioned topic
 
@@ -573,7 +573,7 @@ public void testFailoverSingleAckedPartitionedTopic() throws Exception {
         final int totalMessages = 10;
         final int numberOfPartitions = 3;
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName, numberOfPartitions);
+        admin.topics().createPartitionedTopic(topicName, numberOfPartitions);
         Random rn = new Random();
         // Special step to create partitioned topic
 
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/SubscriptionSeekTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/SubscriptionSeekTest.java
index a66f57ef4d..8cc873328e 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/SubscriptionSeekTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/SubscriptionSeekTest.java
@@ -92,7 +92,7 @@ public void testSeek() throws Exception {
     public void testSeekOnPartitionedTopic() throws Exception {
         final String topicName = "persistent://prop/use/ns-abc/testSeekPartitions";
 
-        admin.persistentTopics().createPartitionedTopic(topicName, 2);
+        admin.topics().createPartitionedTopic(topicName, 2);
         org.apache.pulsar.client.api.Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName)
                 .subscriptionName("my-subscription").subscribe();
 
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/TopicTerminationTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/TopicTerminationTest.java
index 9bc2730643..e096577016 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/TopicTerminationTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/TopicTerminationTest.java
@@ -75,7 +75,7 @@ public void testSimpleTermination() throws Exception {
         /* MessageId msgId2 = */producer.send("test-msg-2".getBytes());
         MessageId msgId3 = producer.send("test-msg-3".getBytes());
 
-        MessageId lastMessageId = admin.persistentTopics().terminateTopicAsync(topicName).get();
+        MessageId lastMessageId = admin.topics().terminateTopicAsync(topicName).get();
         assertEquals(lastMessageId, msgId3);
 
         try {
@@ -97,7 +97,7 @@ public void testCreateProducerOnTerminatedTopic() throws Exception {
         /* MessageId msgId2 = */producer.send("test-msg-2".getBytes());
         MessageId msgId3 = producer.send("test-msg-3".getBytes());
 
-        MessageId lastMessageId = admin.persistentTopics().terminateTopicAsync(topicName).get();
+        MessageId lastMessageId = admin.topics().terminateTopicAsync(topicName).get();
         assertEquals(lastMessageId, msgId3);
 
         try {
@@ -132,7 +132,7 @@ public void testTerminateWhilePublishing() throws Exception {
 
         barrier.await();
 
-        admin.persistentTopics().terminateTopicAsync(topicName).get();
+        admin.topics().terminateTopicAsync(topicName).get();
 
         t.join();
 
@@ -167,20 +167,20 @@ public void testDoubleTerminate() throws Exception {
         /* MessageId msgId2 = */producer.send("test-msg-2".getBytes());
         MessageId msgId3 = producer.send("test-msg-3".getBytes());
 
-        MessageId lastMessageId = admin.persistentTopics().terminateTopicAsync(topicName).get();
+        MessageId lastMessageId = admin.topics().terminateTopicAsync(topicName).get();
         assertEquals(lastMessageId, msgId3);
 
         // Terminate it again
-        lastMessageId = admin.persistentTopics().terminateTopicAsync(topicName).get();
+        lastMessageId = admin.topics().terminateTopicAsync(topicName).get();
         assertEquals(lastMessageId, msgId3);
     }
 
     @Test
     public void testTerminatePartitionedTopic() throws Exception {
-        admin.persistentTopics().createPartitionedTopic(topicName, 4);
+        admin.topics().createPartitionedTopic(topicName, 4);
 
         try {
-            admin.persistentTopics().terminateTopicAsync(topicName).get();
+            admin.topics().terminateTopicAsync(topicName).get();
             fail("Should have failed");
         } catch (ExecutionException ee) {
             assertEquals(ee.getCause().getClass(), NotAllowedException.class);
@@ -207,7 +207,7 @@ public void testSimpleTerminationConsumer() throws Exception {
 
         assertFalse(consumer.hasReachedEndOfTopic());
 
-        MessageId lastMessageId = admin.persistentTopics().terminateTopicAsync(topicName).get();
+        MessageId lastMessageId = admin.topics().terminateTopicAsync(topicName).get();
         assertEquals(lastMessageId, msgId3);
 
         Message<byte[]> msg2 = consumer.receive();
@@ -258,7 +258,7 @@ public void reachedEndOfTopic(Consumer<byte[]> consumer) {
         Thread.sleep(100);
         assertFalse(consumer.hasReachedEndOfTopic());
 
-        MessageId lastMessageId = admin.persistentTopics().terminateTopicAsync(topicName).get();
+        MessageId lastMessageId = admin.topics().terminateTopicAsync(topicName).get();
         assertEquals(lastMessageId, msgId3);
 
         assertTrue(latch.await(3, TimeUnit.SECONDS));
@@ -276,7 +276,7 @@ public void testSimpleTerminationReader() throws Exception {
         MessageId msgId2 = producer.send("test-msg-2".getBytes());
         MessageId msgId3 = producer.send("test-msg-3".getBytes());
 
-        MessageId lastMessageId = admin.persistentTopics().terminateTopicAsync(topicName).get();
+        MessageId lastMessageId = admin.topics().terminateTopicAsync(topicName).get();
         assertEquals(lastMessageId, msgId3);
 
         Reader<byte[]> reader = pulsarClient.newReader().topic(topicName).startMessageId(MessageId.earliest).create();
@@ -327,7 +327,7 @@ public void reachedEndOfTopic(Reader<byte[]> reader) {
         Thread.sleep(100);
         assertFalse(reader.hasReachedEndOfTopic());
 
-        MessageId lastMessageId = admin.persistentTopics().terminateTopicAsync(topicName).get();
+        MessageId lastMessageId = admin.topics().terminateTopicAsync(topicName).get();
         assertEquals(lastMessageId, msgId3);
 
         assertTrue(latch.await(3, TimeUnit.SECONDS));
@@ -343,7 +343,7 @@ public void testSubscribeOnTerminatedTopic() throws Exception {
         /* MessageId msgId1 = */ producer.send("test-msg-1".getBytes());
         MessageId msgId2 = producer.send("test-msg-2".getBytes());
 
-        MessageId lastMessageId = admin.persistentTopics().terminateTopicAsync(topicName).get();
+        MessageId lastMessageId = admin.topics().terminateTopicAsync(topicName).get();
         assertEquals(lastMessageId, msgId2);
 
         org.apache.pulsar.client.api.Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName)
@@ -359,7 +359,7 @@ public void testSubscribeOnTerminatedTopicWithNoMessages() throws Exception {
             .enableBatching(false)
             .messageRoutingMode(MessageRoutingMode.SinglePartition)
             .create();
-        admin.persistentTopics().terminateTopicAsync(topicName).get();
+        admin.topics().terminateTopicAsync(topicName).get();
 
         org.apache.pulsar.client.api.Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName)
                 .subscriptionName("my-sub").subscribe();
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/v1/V1_ReplicatorTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/v1/V1_ReplicatorTest.java
index 276196d155..0d6d3036ef 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/v1/V1_ReplicatorTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/v1/V1_ReplicatorTest.java
@@ -529,6 +529,7 @@ public void testReplicatorClearBacklog() throws Exception {
         consumer1.close();
     }
 
+    @SuppressWarnings("deprecation")
     @Test(enabled = true, timeOut = 30000)
     public void testResetCursorNotFail() throws Exception {
 
@@ -880,6 +881,7 @@ public void verifyChecksumAfterReplication() throws Exception {
      * @param isPartitionedTopic
      * @throws Exception
      */
+    @SuppressWarnings("deprecation")
     @Test(dataProvider = "partitionedTopic")
     public void testReplicatorOnPartitionedTopic(boolean isPartitionedTopic) throws Exception {
 
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/AuthenticatedProducerConsumerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/AuthenticatedProducerConsumerTest.java
index 44c86da52d..d2f6a8d057 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/AuthenticatedProducerConsumerTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/AuthenticatedProducerConsumerTest.java
@@ -229,7 +229,7 @@ public void testAnonymousSyncProducerAndConsumer(int batchMessageDelayMs) throws
         admin.close();
         admin = spy(PulsarAdmin.builder().serviceHttpUrl(brokerUrl.toString()).build());
         admin.namespaces().createNamespace("my-property/my-ns", Sets.newHashSet("test"));
-        admin.persistentTopics().grantPermission("persistent://my-property/my-ns/my-topic", "anonymousUser",
+        admin.topics().grantPermission("persistent://my-property/my-ns/my-topic", "anonymousUser",
                 EnumSet.allOf(AuthAction.class));
 
         // setup the client
@@ -311,7 +311,7 @@ public void testInternalServerExceptionOnLookup() throws Exception {
         mockZookKeeper.shutdown();
         pulsar.getConfiguration().setSuperUserRoles(Sets.newHashSet());
         try {
-            admin.persistentTopics().getPartitionedTopicMetadata(topic);
+            admin.topics().getPartitionedTopicMetadata(topic);
         } catch (PulsarAdminException e) {
             Assert.assertTrue(e.getCause() instanceof InternalServerErrorException);
         }
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/BrokerServiceLookupTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/BrokerServiceLookupTest.java
index 5076d9da66..6a7953cb47 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/BrokerServiceLookupTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/BrokerServiceLookupTest.java
@@ -306,7 +306,7 @@ public void testPartitionTopicLookup() throws Exception {
         int numPartitions = 8;
         TopicName topicName = TopicName.get("persistent://my-property/my-ns/my-partitionedtopic1");
 
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
 
         /**** start broker-2 ****/
         ServiceConfiguration conf2 = new ServiceConfiguration();
@@ -362,7 +362,7 @@ public void testPartitionTopicLookup() throws Exception {
         producer.close();
         consumer.unsubscribe();
         consumer.close();
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
 
         pulsar2.close();
         loadManager2 = null;
@@ -1025,7 +1025,7 @@ public void testPartitionedMetadataWithDeprecatedVersion() throws Exception {
         admin.tenants().createTenant(property,
                 new TenantInfo(Sets.newHashSet("appid1", "appid2"), Sets.newHashSet(cluster)));
         admin.namespaces().createNamespace(property + "/" + cluster + "/" + namespace);
-        admin.persistentTopics().createPartitionedTopic(dest.toString(), totalPartitions);
+        admin.topics().createPartitionedTopic(dest.toString(), totalPartitions);
 
         stopBroker();
         conf.setClientLibraryVersionCheckEnabled(true);
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/DispatcherBlockConsumerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/DispatcherBlockConsumerTest.java
index 7dc315dc97..d8f0cf7d0b 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/DispatcherBlockConsumerTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/DispatcherBlockConsumerTest.java
@@ -46,7 +46,7 @@
 import org.apache.pulsar.broker.service.persistent.PersistentTopic;
 import org.apache.pulsar.client.impl.ConsumerImpl;
 import org.apache.pulsar.client.impl.MessageIdImpl;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.SubscriptionStats;
 import org.apache.pulsar.common.util.collections.ConcurrentOpenHashSet;
 import org.slf4j.Logger;
@@ -523,7 +523,7 @@ public void testBlockDispatcherStats() throws Exception {
             final String subName = "blockDispatch";
             final int timeWaitToSync = 100;
 
-            PersistentTopicStats stats;
+            TopicStats stats;
             SubscriptionStats subStats;
 
             // configure maxUnackMessagePerDispatcher then restart broker to get this change
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/NonPersistentTopicTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/NonPersistentTopicTest.java
index 12bfec5ae9..e96dff4eef 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/NonPersistentTopicTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/NonPersistentTopicTest.java
@@ -26,6 +26,8 @@
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
+import com.google.common.collect.Sets;
+
 import java.lang.reflect.Field;
 import java.net.URL;
 import java.util.Set;
@@ -51,14 +53,14 @@
 import org.apache.pulsar.client.admin.PulsarAdmin;
 import org.apache.pulsar.client.impl.ConsumerImpl;
 import org.apache.pulsar.client.impl.ProducerImpl;
-import org.apache.pulsar.common.naming.TopicName;
 import org.apache.pulsar.common.naming.NamespaceBundle;
+import org.apache.pulsar.common.naming.TopicName;
 import org.apache.pulsar.common.policies.data.ClusterData;
 import org.apache.pulsar.common.policies.data.NonPersistentPublisherStats;
 import org.apache.pulsar.common.policies.data.NonPersistentSubscriptionStats;
 import org.apache.pulsar.common.policies.data.NonPersistentTopicStats;
-import org.apache.pulsar.common.policies.data.TenantInfo;
 import org.apache.pulsar.common.policies.data.SubscriptionStats;
+import org.apache.pulsar.common.policies.data.TenantInfo;
 import org.apache.pulsar.zookeeper.LocalBookkeeperEnsemble;
 import org.apache.pulsar.zookeeper.ZookeeperServerTest;
 import org.slf4j.Logger;
@@ -68,9 +70,6 @@
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
 public class NonPersistentTopicTest extends ProducerConsumerBase {
     private static final Logger log = LoggerFactory.getLogger(NonPersistentTopicTest.class);
     private final String configClusterName = "r1";
@@ -143,7 +142,7 @@ public void testPartitionedNonPersistentTopic(SubscriptionType type) throws Exce
         log.info("-- Starting {} test --", methodName);
 
         final String topic = "non-persistent://my-property/my-ns/partitioned-topic";
-        admin.nonPersistentTopics().createPartitionedTopic(topic, 5);
+        admin.topics().createPartitionedTopic(topic, 5);
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topic).subscriptionName("subscriber-1")
                 .subscriptionType(type).subscribe();
 
@@ -187,7 +186,7 @@ public void testPartitionedNonPersistentTopicWithTcpLookup(SubscriptionType type
 
         final int numPartitions = 5;
         final String topic = "non-persistent://my-property/my-ns/partitioned-topic";
-        admin.nonPersistentTopics().createPartitionedTopic(topic, numPartitions);
+        admin.topics().createPartitionedTopic(topic, numPartitions);
 
         PulsarClient client = PulsarClient.builder().serviceUrl("pulsar://localhost:" + BROKER_PORT)
                 .statsInterval(0, TimeUnit.SECONDS).build();
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/PartitionedProducerConsumerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/PartitionedProducerConsumerTest.java
index 247a7734b9..d8fe3e4c04 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/PartitionedProducerConsumerTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/PartitionedProducerConsumerTest.java
@@ -70,7 +70,7 @@ public void testRoundRobinProducer() throws Exception {
         int numPartitions = 4;
         TopicName topicName = TopicName.get("persistent://my-property/my-ns/my-partitionedtopic1");
 
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
 
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName.toString())
             .enableBatching(false)
@@ -98,7 +98,7 @@ public void testRoundRobinProducer() throws Exception {
         producer.close();
         consumer.unsubscribe();
         consumer.close();
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
 
         log.info("-- Exiting {} test --", methodName);
     }
@@ -111,13 +111,13 @@ public void testPartitionedTopicNameWithSpecialCharacter() throws Exception {
         final String specialCharacter = "! * ' ( ) ; : @ & = + $ , \\ ? % # [ ]";
         TopicName topicName = TopicName
                 .get("persistent://my-property/my-ns/my-partitionedtopic1" + specialCharacter);
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
 
         // Try to create producer which does lookup and create connection with broker
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName.toString())
                 .messageRoutingMode(MessageRoutingMode.RoundRobinPartition).create();
         producer.close();
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
         log.info("-- Exiting {} test --", methodName);
     }
 
@@ -128,7 +128,7 @@ public void testSinglePartitionProducer() throws Exception {
         int numPartitions = 4;
         TopicName topicName = TopicName.get("persistent://my-property/my-ns/my-partitionedtopic2");
 
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
 
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName.toString())
                 .messageRoutingMode(MessageRoutingMode.SinglePartition).create();
@@ -157,7 +157,7 @@ public void testSinglePartitionProducer() throws Exception {
         producer.close();
         consumer.unsubscribe();
         consumer.close();
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
 
         log.info("-- Exiting {} test --", methodName);
     }
@@ -171,7 +171,7 @@ public void testKeyBasedProducer() throws Exception {
         String dummyKey1 = "dummykey1";
         String dummyKey2 = "dummykey2";
 
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
 
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName.toString()).create();
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName.toString())
@@ -203,7 +203,7 @@ public void testKeyBasedProducer() throws Exception {
         producer.close();
         consumer.unsubscribe();
         consumer.close();
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
 
         log.info("-- Exiting {} test --", methodName);
     }
@@ -223,7 +223,7 @@ public void testInvalidSequence() throws Exception {
 
         int numPartitions = 4;
         TopicName topicName = TopicName.get("persistent://my-property/my-ns/my-partitionedtopic4");
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
 
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName.toString())
                 .subscriptionName("my-subscriber-name").subscribe();
@@ -264,7 +264,7 @@ public void testInvalidSequence() throws Exception {
             // ok
         }
 
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
 
     }
 
@@ -273,7 +273,7 @@ public void testSillyUser() throws Exception {
 
         int numPartitions = 4;
         TopicName topicName = TopicName.get("persistent://my-property/my-ns/my-partitionedtopic5");
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
 
         Producer<byte[]> producer = null;
         Consumer<byte[]> consumer = null;
@@ -312,7 +312,7 @@ public void testSillyUser() throws Exception {
             consumer.close();
         }
 
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
 
     }
 
@@ -320,7 +320,7 @@ public void testSillyUser() throws Exception {
     public void testDeletePartitionedTopic() throws Exception {
         int numPartitions = 4;
         TopicName topicName = TopicName.get("persistent://my-property/my-ns/my-partitionedtopic6");
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
 
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName.toString()).create();
         Consumer<byte[]> consumer = pulsarClient.newConsumer().topic(topicName.toString()).subscriptionName("my-sub")
@@ -329,7 +329,7 @@ public void testDeletePartitionedTopic() throws Exception {
         consumer.close();
         producer.close();
 
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
 
         Producer<byte[]> producer1 = pulsarClient.newProducer().topic(topicName.toString()).create();
         if (producer1 instanceof PartitionedProducerImpl) {
@@ -348,7 +348,7 @@ public void testAsyncPartitionedProducerConsumer() throws Exception {
         int numPartitions = 4;
         TopicName topicName = TopicName.get("persistent://my-property/my-ns/my-partitionedtopic1");
 
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName.toString())
             .enableBatching(false)
             .messageRoutingMode(MessageRoutingMode.RoundRobinPartition).create();
@@ -380,7 +380,7 @@ public void testAsyncPartitionedProducerConsumer() throws Exception {
         producer.close();
         consumer.unsubscribe();
         consumer.close();
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
 
         log.info("-- Exiting {} test --", methodName);
     }
@@ -396,7 +396,7 @@ public void testAsyncPartitionedProducerConsumerQueueSizeOne() throws Exception
         int numPartitions = 4;
         TopicName topicName = TopicName.get("persistent://my-property/my-ns/my-partitionedtopic1");
 
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
 
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName.toString())
             .enableBatching(false)
@@ -429,7 +429,7 @@ public void testAsyncPartitionedProducerConsumerQueueSizeOne() throws Exception
         producer.close();
         consumer.unsubscribe();
         consumer.close();
-        admin.persistentTopics().deletePartitionedTopic(topicName.toString());
+        admin.topics().deletePartitionedTopic(topicName.toString());
 
         log.info("-- Exiting {} test --", methodName);
     }
@@ -449,7 +449,7 @@ public void testFairDistributionForPartitionConsumers() throws Exception {
         final String producer2Msg = "producer2";
         final int queueSize = 10;
 
-        admin.persistentTopics().createPartitionedTopic(topicName, numPartitions);
+        admin.topics().createPartitionedTopic(topicName, numPartitions);
 
         Producer<byte[]> producer1 = pulsarClient.newProducer().topic(topicName + "-partition-0")
                 .messageRoutingMode(MessageRoutingMode.RoundRobinPartition).create();
@@ -491,7 +491,7 @@ public void testFairDistributionForPartitionConsumers() throws Exception {
         producer2.close();
         consumer.unsubscribe();
         consumer.close();
-        admin.persistentTopics().deletePartitionedTopic(topicName);
+        admin.topics().deletePartitionedTopic(topicName);
 
         log.info("-- Exiting {} test --", methodName);
     }
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerStatTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerStatTest.java
index fb2c2b40ac..2c94966aa3 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerStatTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerStatTest.java
@@ -352,7 +352,7 @@ public void testBatchMessagesRateOut() throws PulsarClientException, Interrupted
         Thread.sleep(2000); // Two seconds sleep
         runTest.set(false);
         pulsar.getBrokerService().updateRates();
-        double actualRate = admin.persistentTopics().getStats(topicName).msgRateOut;
+        double actualRate = admin.topics().getStats(topicName).msgRateOut;
         assertTrue(actualRate > (produceRate / batchSize));
         consumer.unsubscribe();
         log.info("-- Exiting {} test --", methodName);
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerTest.java
index 7ec9cf4d85..0229afe261 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/SimpleProducerConsumerTest.java
@@ -2078,7 +2078,7 @@ public void testFailReceiveAsyncOnConsumerClose() throws Exception {
         // (2) Partitioned-consumer
         int numPartitions = 4;
         TopicName topicName = TopicName.get("persistent://my-property/my-ns/failAsyncReceive");
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
         Consumer<byte[]> partitionedConsumer = pulsarClient.newConsumer().topic(topicName.toString())
                 .subscriptionName("my-partitioned-subscriber").subscribe();
         partitionedConsumer.close();
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TopicReaderTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TopicReaderTest.java
index 3097a93bff..6318974e22 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TopicReaderTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TopicReaderTest.java
@@ -34,7 +34,7 @@
 import java.util.concurrent.TimeUnit;
 import org.apache.pulsar.client.impl.BatchMessageIdImpl;
 import org.apache.pulsar.client.impl.MessageImpl;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -163,16 +163,16 @@ public void testTopicStats() throws Exception {
 
         Reader<byte[]> reader2 = pulsarClient.newReader().topic(topicName).startMessageId(MessageId.earliest).create();
 
-        PersistentTopicStats stats = admin.persistentTopics().getStats(topicName);
+        TopicStats stats = admin.topics().getStats(topicName);
         assertEquals(stats.subscriptions.size(), 2);
 
         reader1.close();
-        stats = admin.persistentTopics().getStats(topicName);
+        stats = admin.topics().getStats(topicName);
         assertEquals(stats.subscriptions.size(), 1);
 
         reader2.close();
 
-        stats = admin.persistentTopics().getStats(topicName);
+        stats = admin.topics().getStats(topicName);
         assertEquals(stats.subscriptions.size(), 0);
     }
 
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/v1/V1_ProducerConsumerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/v1/V1_ProducerConsumerTest.java
index 36238ba77f..dd768b0798 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/v1/V1_ProducerConsumerTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/v1/V1_ProducerConsumerTest.java
@@ -2175,7 +2175,7 @@ public void testFailReceiveAsyncOnConsumerClose() throws Exception {
         // (2) Partitioned-consumer
         int numPartitions = 4;
         TopicName topicName = TopicName.get("persistent://my-property/use/my-ns/failAsyncReceive");
-        admin.persistentTopics().createPartitionedTopic(topicName.toString(), numPartitions);
+        admin.topics().createPartitionedTopic(topicName.toString(), numPartitions);
         Consumer partitionedConsumer = pulsarClient.subscribe(topicName.toString(), "my-partitioned-subscriber",
                 new ConsumerConfiguration());
         partitionedConsumer.close();
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/BrokerClientIntegrationTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/BrokerClientIntegrationTest.java
index 2156bb38f2..bf65fdd157 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/BrokerClientIntegrationTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/BrokerClientIntegrationTest.java
@@ -441,14 +441,14 @@ public void testResetCursor(SubscriptionType subType) throws Exception {
 
         log.info("reset cursor to " + timestamp + " for topic " + topicName.toString() + " for subs " + subsId);
         log.info("issuing admin operation on " + admin.getServiceUrl());
-        List<String> subList = admin.persistentTopics().getSubscriptions(topicName.toString());
+        List<String> subList = admin.topics().getSubscriptions(topicName.toString());
         for (String subs : subList) {
             log.info("got sub " + subs);
         }
         publishTimeIdMap.clear();
         // reset the cursor to this timestamp
         Assert.assertTrue(subList.contains(subsId));
-        admin.persistentTopics().resetCursor(topicName.toString(), subsId, timestamp);
+        admin.topics().resetCursor(topicName.toString(), subsId, timestamp);
 
         Thread.sleep(3000);
         int totalExpected = 0;
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/MessageIdTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/MessageIdTest.java
index 48775e0611..187803db43 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/MessageIdTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/MessageIdTest.java
@@ -182,7 +182,7 @@ public void partitionedProducerSendAsync() throws PulsarClientException, PulsarA
         final String messagePredicate = "my-message-" + key + "-";
         final int numberOfMessages = 30;
         int numberOfPartitions = 3;
-        admin.persistentTopics().createPartitionedTopic(topicName, numberOfPartitions);
+        admin.topics().createPartitionedTopic(topicName, numberOfPartitions);
 
         // 2. Create Producer
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName)
@@ -234,7 +234,7 @@ public void partitionedProducerSend() throws PulsarClientException, PulsarAdminE
         final String messagePredicate = "my-message-" + key + "-";
         final int numberOfMessages = 30;
         int numberOfPartitions = 7;
-        admin.persistentTopics().createPartitionedTopic(topicName, numberOfPartitions);
+        admin.topics().createPartitionedTopic(topicName, numberOfPartitions);
 
         // 2. Create Producer
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName)
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/PatternTopicsConsumerImplTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/PatternTopicsConsumerImplTest.java
index 8e7c71db8a..a12c83c700 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/PatternTopicsConsumerImplTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/PatternTopicsConsumerImplTest.java
@@ -79,8 +79,8 @@ public void testPatternTopicsSubscribeWithBuilderFail() throws Exception {
         Pattern pattern = Pattern.compile(patternString);
 
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // test failing builder with pattern and topic should fail
         try {
@@ -137,8 +137,8 @@ public void testBinaryProtoToGetTopicsOfNamespace() throws Exception {
 
         // 1. create partition
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 2. create producer
         String messagePredicate = "my-message-" + key + "-";
@@ -282,8 +282,8 @@ public void testStartEmptyPatternConsumer() throws Exception {
 
         // 1. create partition
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 2. Create consumer, this should success, but with empty sub-consumser internal
         Consumer<byte[]> consumer = pulsarClient.newConsumer()
@@ -369,8 +369,8 @@ public void testAutoSubscribePatternConsumer() throws Exception {
 
         // 1. create partition
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 2. create producer
         String messagePredicate = "my-message-" + key + "-";
@@ -426,7 +426,7 @@ public void testAutoSubscribePatternConsumer() throws Exception {
 
         // 6. create another producer with 4 partitions
         String topicName4 = "persistent://my-property/my-ns/pattern-topic-4-" + key;
-        admin.persistentTopics().createPartitionedTopic(topicName4, 4);
+        admin.topics().createPartitionedTopic(topicName4, 4);
         Producer<byte[]> producer4 = pulsarClient.newProducer().topic(topicName4)
             .enableBatching(false)
             .messageRoutingMode(org.apache.pulsar.client.api.MessageRoutingMode.RoundRobinPartition)
@@ -477,8 +477,8 @@ public void testAutoUnbubscribePatternConsumer() throws Exception {
 
         // 1. create partition
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 2. create producer
         String messagePredicate = "my-message-" + key + "-";
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/PerMessageUnAcknowledgedRedeliveryTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/PerMessageUnAcknowledgedRedeliveryTest.java
index 14ed49a48e..87f8d366dc 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/PerMessageUnAcknowledgedRedeliveryTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/PerMessageUnAcknowledgedRedeliveryTest.java
@@ -361,7 +361,7 @@ public void testSharedAckedPartitionedTopic() throws Exception {
         final int totalMessages = 15;
         final int numberOfPartitions = 3;
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName, numberOfPartitions);
+        admin.topics().createPartitionedTopic(topicName, numberOfPartitions);
 
         // 1. producer connect
         Producer<byte[]> producer = pulsarClient.newProducer().topic(topicName)
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/TopicsConsumerImplTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/TopicsConsumerImplTest.java
index 8ff745fdad..d28d1d5f33 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/TopicsConsumerImplTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/TopicsConsumerImplTest.java
@@ -77,8 +77,8 @@ public void testDifferentTopicsNameSubscribe() throws Exception {
         List<String> topicNames = Lists.newArrayList(topicName1, topicName2, topicName3);
 
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 2. Create consumer
         try {
@@ -105,8 +105,8 @@ public void testGetConsumersAndGetTopics() throws Exception {
         List<String> topicNames = Lists.newArrayList(topicName1, topicName2);
 
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 2. Create consumer
         Consumer<byte[]> consumer = pulsarClient.newConsumer()
@@ -147,8 +147,8 @@ public void testSyncProducerAndConsumer() throws Exception {
         List<String> topicNames = Lists.newArrayList(topicName1, topicName2, topicName3);
 
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 1. producer connect
         Producer<byte[]> producer1 = pulsarClient.newProducer().topic(topicName1)
@@ -212,8 +212,8 @@ public void testAsyncConsumer() throws Exception {
         List<String> topicNames = Lists.newArrayList(topicName1, topicName2, topicName3);
 
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 1. producer connect
         Producer<byte[]> producer1 = pulsarClient.newProducer().topic(topicName1)
@@ -295,8 +295,8 @@ public void testConsumerUnackedRedelivery() throws Exception {
         List<String> topicNames = Lists.newArrayList(topicName1, topicName2, topicName3);
 
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 1. producer connect
         Producer<byte[]> producer1 = pulsarClient.newProducer().topic(topicName1)
@@ -439,8 +439,8 @@ public void testSubscribeUnsubscribeSingleTopic() throws Exception {
         List<String> topicNames = Lists.newArrayList(topicName1, topicName2, topicName3);
 
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // 1. producer connect
         Producer<byte[]> producer1 = pulsarClient.newProducer().topic(topicName1)
@@ -563,8 +563,8 @@ public void testTopicsNameSubscribeWithBuilderFail() throws Exception {
         final String topicName3 = "persistent://prop/use/ns-abc/topic-3-" + key;
 
         admin.tenants().createTenant("prop", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topicName2, 2);
-        admin.persistentTopics().createPartitionedTopic(topicName3, 3);
+        admin.topics().createPartitionedTopic(topicName2, 2);
+        admin.topics().createPartitionedTopic(topicName3, 3);
 
         // test failing builder with empty topics
         try {
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/UnAcknowledgedMessagesTimeoutTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/UnAcknowledgedMessagesTimeoutTest.java
index 42c957f09c..0b5126b00f 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/UnAcknowledgedMessagesTimeoutTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/UnAcknowledgedMessagesTimeoutTest.java
@@ -167,7 +167,7 @@ public void testSharedSingleAckedPartitionedTopic() throws Exception {
         final String messagePredicate = "my-message-" + key + "-";
         final int totalMessages = 20;
         final int numberOfPartitions = 3;
-        admin.persistentTopics().createPartitionedTopic(topicName, numberOfPartitions);
+        admin.topics().createPartitionedTopic(topicName, numberOfPartitions);
         // Special step to create partitioned topic
 
         // 1. producer connect
@@ -255,7 +255,7 @@ public void testFailoverSingleAckedPartitionedTopic() throws Exception {
         final String messagePredicate = "my-message-" + key + "-";
         final int totalMessages = 10;
         final int numberOfPartitions = 3;
-        admin.persistentTopics().createPartitionedTopic(topicName, numberOfPartitions);
+        admin.topics().createPartitionedTopic(topicName, numberOfPartitions);
         // Special step to create partitioned topic
 
         // 1. producer connect
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/ZeroQueueSizeTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/ZeroQueueSizeTest.java
index 2f23554c5d..725fa46927 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/ZeroQueueSizeTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/ZeroQueueSizeTest.java
@@ -81,7 +81,7 @@ public void zeroQueueSizePartitionedTopicInCompatibility() throws PulsarClientEx
         final String topicName = "persistent://prop/use/ns-abc/topic-" + key;
         final String subscriptionName = "my-ex-subscription-" + key;
         int numberOfPartitions = 3;
-        admin.persistentTopics().createPartitionedTopic(topicName, numberOfPartitions);
+        admin.topics().createPartitionedTopic(topicName, numberOfPartitions);
         pulsarClient.newConsumer().topic(topicName).subscriptionName(subscriptionName).receiverQueueSize(0).subscribe();
     }
 
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/websocket/proxy/ProxyAuthorizationTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/websocket/proxy/ProxyAuthorizationTest.java
index ed51a45662..f9ac9eb8f5 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/websocket/proxy/ProxyAuthorizationTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/websocket/proxy/ProxyAuthorizationTest.java
@@ -95,7 +95,7 @@ public void test() throws Exception {
         assertEquals(auth.canLookup(TopicName.get("persistent://p1/c1/ns1/ds1"), "my-role", null), true);
         assertEquals(auth.canProduce(TopicName.get("persistent://p1/c1/ns1/ds1"), "my-role", null), true);
 
-        admin.persistentTopics().grantPermission("persistent://p1/c1/ns1/ds2", "other-role",
+        admin.topics().grantPermission("persistent://p1/c1/ns1/ds2", "other-role",
                 EnumSet.of(AuthAction.consume));
         waitForChange();
 
diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/websocket/proxy/ProxyPublishConsumeTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/websocket/proxy/ProxyPublishConsumeTest.java
index 23895fcccb..c05c356a3c 100644
--- a/pulsar-broker/src/test/java/org/apache/pulsar/websocket/proxy/ProxyPublishConsumeTest.java
+++ b/pulsar-broker/src/test/java/org/apache/pulsar/websocket/proxy/ProxyPublishConsumeTest.java
@@ -339,8 +339,8 @@ public void producerBacklogQuotaExceededTest() throws Exception {
                     HttpServletResponse.SC_SERVICE_UNAVAILABLE);
         } finally {
             stopWebSocketClient(produceClient2);
-            admin.persistentTopics().skipAllMessages("persistent://" + topic, subscription);
-            admin.persistentTopics().delete("persistent://" + topic);
+            admin.topics().skipAllMessages("persistent://" + topic, subscription);
+            admin.topics().delete("persistent://" + topic);
             admin.namespaces().removeBacklogQuota(namespace);
             admin.namespaces().deleteNamespace(namespace);
         }
diff --git a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/NonPersistentTopics.java b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/NonPersistentTopics.java
index 2bef50192e..31e602c492 100644
--- a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/NonPersistentTopics.java
+++ b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/NonPersistentTopics.java
@@ -27,6 +27,10 @@
 import org.apache.pulsar.common.policies.data.NonPersistentTopicStats;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
 
+/**
+ * @deprecated since 2.0. See {@link Topics}
+ */
+@Deprecated
 public interface NonPersistentTopics {
 
 
diff --git a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PersistentTopics.java b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PersistentTopics.java
index 006bfaac0d..b65ffd09e1 100644
--- a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PersistentTopics.java
+++ b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PersistentTopics.java
@@ -18,903 +18,10 @@
  */
 package org.apache.pulsar.client.admin;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-
-import org.apache.pulsar.client.admin.PulsarAdminException.ConflictException;
-import org.apache.pulsar.client.admin.PulsarAdminException.NotAllowedException;
-import org.apache.pulsar.client.admin.PulsarAdminException.NotAuthorizedException;
-import org.apache.pulsar.client.admin.PulsarAdminException.NotFoundException;
-import org.apache.pulsar.client.admin.PulsarAdminException.PreconditionFailedException;
-import org.apache.pulsar.client.api.Message;
-import org.apache.pulsar.client.api.MessageId;
-import org.apache.pulsar.common.compaction.CompactionStatus;
-import org.apache.pulsar.common.partition.PartitionedTopicMetadata;
-import org.apache.pulsar.common.policies.data.AuthAction;
-import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
-import org.omg.CosNaming.NamingContextPackage.NotFound;
-
-import com.google.gson.JsonObject;
-
-public interface PersistentTopics {
-
-    /**
-     * Get the list of topics under a namespace.
-     * <p>
-     * Response example:
-     *
-     * <pre>
-     * <code>["topic://my-tenant/use/my-namespace/topic-1",
-     *  "topic://my-tenant/use/my-namespace/topic-2"]</code>
-     * </pre>
-     *
-     * @param namespace
-     *            Namespace name
-     * @return a list of topics
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Namespace does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    List<String> getList(String namespace) throws PulsarAdminException;
-
-    /**
-     * Get the list of partitioned topics under a namespace.
-     * <p>
-     * Response example:
-     *
-     * <pre>
-     * <code>["persistent://my-tenant/use/my-namespace/topic-1",
-     *  "persistent://my-tenant/use/my-namespace/topic-2"]</code>
-     * </pre>
-     *
-     * @param namespace
-     *            Namespace name
-     * @return a list of partitioned topics
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Namespace does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    List<String> getPartitionedTopicList(String namespace) throws PulsarAdminException;
-
-    /**
-     * Get permissions on a topic.
-     * <p>
-     * Retrieve the effective permissions for a topic. These permissions are defined by the permissions set at the
-     * namespace level combined (union) with any eventual specific permission set on the topic.
-     * <p>
-     * Response Example:
-     *
-     * <pre>
-     * <code>{
-     *   "role-1" : [ "produce" ],
-     *   "role-2" : [ "consume" ]
-     * }</code>
-     * </pre>
-     *
-     * @param topic
-     *            Topic url
-     * @return a map of topics an their permissions set
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Namespace does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    Map<String, Set<AuthAction>> getPermissions(String topic) throws PulsarAdminException;
-
-    /**
-     * Grant permission on a topic.
-     * <p>
-     * Grant a new permission to a client role on a single topic.
-     * <p>
-     * Request parameter example:
-     *
-     * <pre>
-     * <code>["produce", "consume"]</code>
-     * </pre>
-     *
-     * @param topic
-     *            Topic url
-     * @param role
-     *            Client role to which grant permission
-     * @param actions
-     *            Auth actions (produce and consume)
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Namespace does not exist
-     * @throws ConflictException
-     *             Concurrent modification
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void grantPermission(String topic, String role, Set<AuthAction> actions) throws PulsarAdminException;
-
-    /**
-     * Revoke permissions on a topic.
-     * <p>
-     * Revoke permissions to a client role on a single topic. If the permission was not set at the topic level, but
-     * rather at the namespace level, this operation will return an error (HTTP status code 412).
-     *
-     * @param topic
-     *            Topic url
-     * @param role
-     *            Client role to which remove permission
-     * @throws UniformInterfaceException
-     *             if the operations was not successful
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFound
-     *             Namespace does not exist
-     * @throws PreconditionFailedException
-     *             Permissions are not set at the topic level
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void revokePermissions(String topic, String role) throws PulsarAdminException;
-
-    /**
-     * Create a partitioned topic.
-     * <p>
-     * Create a partitioned topic. It needs to be called before creating a producer for a partitioned topic.
-     * <p>
-     *
-     * @param topic
-     *            Topic name
-     * @param numPartitions
-     *            Number of partitions to create of the topic
-     * @throws PulsarAdminException
-     */
-    void createPartitionedTopic(String topic, int numPartitions) throws PulsarAdminException;
-
-    /**
-     * Create a partitioned topic asynchronously.
-     * <p>
-     * Create a partitioned topic asynchronously. It needs to be called before creating a producer for a partitioned
-     * topic.
-     * <p>
-     *
-     * @param topic
-     *            Topic name
-     * @param numPartitions
-     *            Number of partitions to create of the topic
-     * @return a future that can be used to track when the partitioned topic is created
-     */
-    CompletableFuture<Void> createPartitionedTopicAsync(String topic, int numPartitions);
-
-    /**
-     * Update number of partitions of a non-global partitioned topic.
-     * <p>
-     * It requires partitioned-topic to be already exist and number of new partitions must be greater than existing
-     * number of partitions. Decrementing number of partitions requires deletion of topic which is not supported.
-     * <p>
-     *
-     * @param topic
-     *            Topic name
-     * @param numPartitions
-     *            Number of new partitions of already exist partitioned-topic
-     *
-     * @return a future that can be used to track when the partitioned topic is updated
-     */
-    void updatePartitionedTopic(String topic, int numPartitions) throws PulsarAdminException;
-
-    /**
-     * Update number of partitions of a non-global partitioned topic asynchronously.
-     * <p>
-     * It requires partitioned-topic to be already exist and number of new partitions must be greater than existing
-     * number of partitions. Decrementing number of partitions requires deletion of topic which is not supported.
-     * <p>
-     *
-     * @param topic
-     *            Topic name
-     * @param numPartitions
-     *            Number of new partitions of already exist partitioned-topic
-     *
-     * @return a future that can be used to track when the partitioned topic is updated
-     */
-    CompletableFuture<Void> updatePartitionedTopicAsync(String topic, int numPartitions);
-
-    /**
-     * Get metadata of a partitioned topic.
-     * <p>
-     * Get metadata of a partitioned topic.
-     * <p>
-     *
-     * @param topic
-     *            Topic name
-     * @return Partitioned topic metadata
-     * @throws PulsarAdminException
-     */
-    PartitionedTopicMetadata getPartitionedTopicMetadata(String topic) throws PulsarAdminException;
-
-    /**
-     * Get metadata of a partitioned topic asynchronously.
-     * <p>
-     * Get metadata of a partitioned topic asynchronously.
-     * <p>
-     *
-     * @param topic
-     *            Topic name
-     * @return a future that can be used to track when the partitioned topic metadata is returned
-     */
-    CompletableFuture<PartitionedTopicMetadata> getPartitionedTopicMetadataAsync(String topic);
-
-    /**
-     * Delete a partitioned topic.
-     * <p>
-     * It will also delete all the partitions of the topic if it exists.
-     * <p>
-     *
-     * @param topic
-     *            Topic name
-     * @throws PulsarAdminException
-     */
-    void deletePartitionedTopic(String topic) throws PulsarAdminException;
-
-    /**
-     * Delete a partitioned topic asynchronously.
-     * <p>
-     * It will also delete all the partitions of the topic if it exists.
-     * <p>
-     *
-     * @param topic
-     *            Topic name
-     * @return a future that can be used to track when the partitioned topic is deleted
-     */
-    CompletableFuture<Void> deletePartitionedTopicAsync(String topic);
-
-    /**
-     * Delete a topic.
-     * <p>
-     * Delete a topic. The topic cannot be deleted if there's any active subscription or producer connected to the it.
-     * <p>
-     *
-     * @param topic
-     *            Topic name
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic does not exist
-     * @throws PreconditionFailedException
-     *             Topic has active subscriptions or producers
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void delete(String topic) throws PulsarAdminException;
-
-    /**
-     * Delete a topic asynchronously.
-     * <p>
-     * Delete a topic asynchronously. The topic cannot be deleted if there's any active subscription or producer
-     * connected to the it.
-     * <p>
-     *
-     * @param topic
-     *            topic name
-     *
-     * @return a future that can be used to track when the topic is deleted
-     */
-    CompletableFuture<Void> deleteAsync(String topic);
-
-    /**
-     * Unload a topic.
-     * <p>
-     *
-     * @param topic
-     *            topic name
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             topic does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void unload(String topic) throws PulsarAdminException;
-
-    /**
-     * Unload a topic asynchronously.
-     * <p>
-     *
-     * @param topic
-     *            topic name
-     *
-     * @return a future that can be used to track when the topic is unloaded
-     */
-    CompletableFuture<Void> unloadAsync(String topic);
-
-    /**
-     * Terminate the topic and prevent any more messages being published on it.
-     * <p>
-     * This
-     *
-     * @param topic
-     *            topic name
-     * @return the message id of the last message that was published in the topic
-     */
-    CompletableFuture<MessageId> terminateTopicAsync(String topic);
-
-    /**
-     * Get the list of subscriptions.
-     * <p>
-     * Get the list of persistent subscriptions for a given topic.
-     * <p>
-     *
-     * @param topic
-     *            topic name
-     * @return the list of subscriptions
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    List<String> getSubscriptions(String topic) throws PulsarAdminException;
-
-    /**
-     * Get the list of subscriptions asynchronously.
-     * <p>
-     * Get the list of persistent subscriptions for a given topic.
-     * <p>
-     *
-     * @param topic
-     *            topic name
-     * @return a future that can be used to track when the list of subscriptions is returned
-     */
-    CompletableFuture<List<String>> getSubscriptionsAsync(String topic);
-
-    /**
-     * Get the stats for the topic.
-     * <p>
-     * Response Example:
-     *
-     * <pre>
-     * <code>
-     * {
-     *   "msgRateIn" : 100.0,                    // Total rate of messages published on the topic. msg/s
-     *   "msgThroughputIn" : 10240.0,            // Total throughput of messages published on the topic. byte/s
-     *   "msgRateOut" : 100.0,                   // Total rate of messages delivered on the topic. msg/s
-     *   "msgThroughputOut" : 10240.0,           // Total throughput of messages delivered on the topic. byte/s
-     *   "averageMsgSize" : 1024.0,              // Average size of published messages. bytes
-     *   "publishers" : [                        // List of publishes on this topic with their stats
-     *      {
-     *          "producerId" : 10                // producer id
-     *          "address"   : 10.4.1.23:3425     // IP and port for this producer
-     *          "connectedSince" : 2014-11-21 23:54:46 // Timestamp of this published connection
-     *          "msgRateIn" : 100.0,             // Total rate of messages published by this producer. msg/s
-     *          "msgThroughputIn" : 10240.0,     // Total throughput of messages published by this producer. byte/s
-     *          "averageMsgSize" : 1024.0,       // Average size of published messages by this producer. bytes
-     *      },
-     *   ],
-     *   "subscriptions" : {                     // Map of subscriptions on this topic
-     *     "sub1" : {
-     *       "msgRateOut" : 100.0,               // Total rate of messages delivered on this subscription. msg/s
-     *       "msgThroughputOut" : 10240.0,       // Total throughput delivered on this subscription. bytes/s
-     *       "msgBacklog" : 0,                   // Number of messages in the subscriotion backlog
-     *       "type" : Exclusive                  // Whether the subscription is exclusive or shared
-     *       "consumers" [                       // List of consumers on this subscription
-     *          {
-     *              "id" : 5                            // Consumer id
-     *              "address" : 10.4.1.23:3425          // IP and port for this consumer
-     *              "connectedSince" : 2014-11-21 23:54:46 // Timestamp of this consumer connection
-     *              "msgRateOut" : 100.0,               // Total rate of messages delivered to this consumer. msg/s
-     *              "msgThroughputOut" : 10240.0,       // Total throughput delivered to this consumer. bytes/s
-     *          }
-     *       ],
-     *   },
-     *   "replication" : {                    // Replication statistics
-     *     "cluster_1" : {                    // Cluster name in the context of from-cluster or to-cluster
-     *       "msgRateIn" : 100.0,             // Total rate of messages received from this remote cluster. msg/s
-     *       "msgThroughputIn" : 10240.0,     // Total throughput received from this remote cluster. bytes/s
-     *       "msgRateOut" : 100.0,            // Total rate of messages delivered to the replication-subscriber. msg/s
-     *       "msgThroughputOut" : 10240.0,    // Total throughput delivered to the replication-subscriber. bytes/s
-     *       "replicationBacklog" : 0,        // Number of messages pending to be replicated to this remote cluster
-     *       "connected" : true,              // Whether the replication-subscriber is currently connected locally
-     *     },
-     *     "cluster_2" : {
-     *       "msgRateIn" : 100.0,
-     *       "msgThroughputIn" : 10240.0,
-     *       "msgRateOut" : 100.0,
-     *       "msghroughputOut" : 10240.0,
-     *       "replicationBacklog" : 0,
-     *       "connected" : true,
-     *     }
-     *   },
-     * }
-     * </code>
-     * </pre>
-     *
-     * All the rates are computed over a 1 minute window and are relative the last completed 1 minute period.
-     *
-     * @param topic
-     *            topic name
-     * @return the topic statistics
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    PersistentTopicStats getStats(String topic) throws PulsarAdminException;
-
-    /**
-     * Get the stats for the topic asynchronously. All the rates are computed over a 1 minute window and are relative
-     * the last completed 1 minute period.
-     *
-     * @param topic
-     *            topic name
-     *
-     * @return a future that can be used to track when the topic statistics are returned
-     *
-     */
-    CompletableFuture<PersistentTopicStats> getStatsAsync(String topic);
-
-    /**
-     * Get the internal stats for the topic.
-     * <p>
-     * Access the internal state of the topic
-     *
-     * @param topic
-     *            topic name
-     * @return the topic statistics
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    PersistentTopicInternalStats getInternalStats(String topic) throws PulsarAdminException;
-
-    /**
-     * Get the internal stats for the topic asynchronously.
-     *
-     * @param topic
-     *            topic Name
-     *
-     * @return a future that can be used to track when the internal topic statistics are returned
-     */
-    CompletableFuture<PersistentTopicInternalStats> getInternalStatsAsync(String topic);
-
-    /**
-     * Get a JSON representation of the topic metadata stored in ZooKeeper
-     *
-     * @param topic
-     *            topic name
-     * @return the topic internal metadata
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    JsonObject getInternalInfo(String topic) throws PulsarAdminException;
-
-    /**
-     * Get a JSON representation of the topic metadata stored in ZooKeeper
-     *
-     * @param topic
-     *            topic name
-     * @return a future to receive the topic internal metadata
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    CompletableFuture<JsonObject> getInternalInfoAsync(String topic);
-
-    /**
-     * Get the stats for the partitioned topic
-     * <p>
-     * Response Example:
-     *
-     * <pre>
-     * <code>
-     * {
-     *   "msgRateIn" : 100.0,                    // Total rate of messages published on the partitioned topic. msg/s
-     *   "msgThroughputIn" : 10240.0,            // Total throughput of messages published on the partitioned topic. byte/s
-     *   "msgRateOut" : 100.0,                   // Total rate of messages delivered on the partitioned topic. msg/s
-     *   "msgThroughputOut" : 10240.0,           // Total throughput of messages delivered on the partitioned topic. byte/s
-     *   "averageMsgSize" : 1024.0,              // Average size of published messages. bytes
-     *   "publishers" : [                        // List of publishes on this partitioned topic with their stats
-     *      {
-     *          "msgRateIn" : 100.0,             // Total rate of messages published by this producer. msg/s
-     *          "msgThroughputIn" : 10240.0,     // Total throughput of messages published by this producer. byte/s
-     *          "averageMsgSize" : 1024.0,       // Average size of published messages by this producer. bytes
-     *      },
-     *   ],
-     *   "subscriptions" : {                     // Map of subscriptions on this topic
-     *     "sub1" : {
-     *       "msgRateOut" : 100.0,               // Total rate of messages delivered on this subscription. msg/s
-     *       "msgThroughputOut" : 10240.0,       // Total throughput delivered on this subscription. bytes/s
-     *       "msgBacklog" : 0,                   // Number of messages in the subscriotion backlog
-     *       "type" : Exclusive                  // Whether the subscription is exclusive or shared
-     *       "consumers" [                       // List of consumers on this subscription
-     *          {
-     *              "msgRateOut" : 100.0,               // Total rate of messages delivered to this consumer. msg/s
-     *              "msgThroughputOut" : 10240.0,       // Total throughput delivered to this consumer. bytes/s
-     *          }
-     *       ],
-     *   },
-     *   "replication" : {                    // Replication statistics
-     *     "cluster_1" : {                    // Cluster name in the context of from-cluster or to-cluster
-     *       "msgRateIn" : 100.0,             // Total rate of messages received from this remote cluster. msg/s
-     *       "msgThroughputIn" : 10240.0,     // Total throughput received from this remote cluster. bytes/s
-     *       "msgRateOut" : 100.0,            // Total rate of messages delivered to the replication-subscriber. msg/s
-     *       "msgThroughputOut" : 10240.0,    // Total throughput delivered to the replication-subscriber. bytes/s
-     *       "replicationBacklog" : 0,        // Number of messages pending to be replicated to this remote cluster
-     *       "connected" : true,              // Whether the replication-subscriber is currently connected locally
-     *     },
-     *     "cluster_2" : {
-     *       "msgRateIn" : 100.0,
-     *       "msgThroughputIn" : 10240.0,
-     *       "msgRateOut" : 100.0,
-     *       "msghroughputOut" : 10240.0,
-     *       "replicationBacklog" : 0,
-     *       "connected" : true,
-     *     }
-     *   },
-     * }
-     * </code>
-     * </pre>
-     *
-     * All the rates are computed over a 1 minute window and are relative the last completed 1 minute period.
-     *
-     * @param topic
-     *            topic name
-     * @param perPartition
-     *
-     * @return the partitioned topic statistics
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     *
-     */
-    PartitionedTopicStats getPartitionedStats(String topic, boolean perPartition) throws PulsarAdminException;
-
-    /**
-     * Get the stats for the partitioned topic asynchronously
-     *
-     * @param topic
-     *            topic Name
-     * @param perPartition
-     *            flag to get stats per partition
-     * @return a future that can be used to track when the partitioned topic statistics are returned
-     */
-    CompletableFuture<PartitionedTopicStats> getPartitionedStatsAsync(String topic, boolean perPartition);
-
-    /**
-     * Delete a subscription.
-     * <p>
-     * Delete a persistent subscription from a topic. There should not be any active consumers on the subscription.
-     * <p>
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic or subscription does not exist
-     * @throws PreconditionFailedException
-     *             Subscription has active consumers
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void deleteSubscription(String topic, String subName) throws PulsarAdminException;
-
-    /**
-     * Delete a subscription asynchronously.
-     * <p>
-     * Delete a persistent subscription from a topic. There should not be any active consumers on the subscription.
-     * <p>
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     *
-     * @return a future that can be used to track when the subscription is deleted
-     */
-    CompletableFuture<Void> deleteSubscriptionAsync(String topic, String subName);
-
-    /**
-     * Skip all messages on a topic subscription.
-     * <p>
-     * Completely clears the backlog on the subscription.
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic or subscription does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void skipAllMessages(String topic, String subName) throws PulsarAdminException;
-
-    /**
-     * Skip all messages on a topic subscription asynchronously.
-     * <p>
-     * Completely clears the backlog on the subscription.
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     *
-     * @return a future that can be used to track when all the messages are skipped
-     */
-    CompletableFuture<Void> skipAllMessagesAsync(String topic, String subName);
-
-    /**
-     * Skip messages on a topic subscription.
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param numMessages
-     *            Number of messages
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic or subscription does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void skipMessages(String topic, String subName, long numMessages) throws PulsarAdminException;
-
-    /**
-     * Skip messages on a topic subscription asynchronously.
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param numMessages
-     *            Number of messages
-     *
-     * @return a future that can be used to track when the number of messages are skipped
-     */
-    CompletableFuture<Void> skipMessagesAsync(String topic, String subName, long numMessages);
-
-    /**
-     * Expire all messages older than given N (expireTimeInSeconds) seconds for a given subscription
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param expireTimeInSeconds
-     *            Expire messages older than time in seconds
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    public void expireMessages(String topic, String subscriptionName, long expireTimeInSeconds)
-            throws PulsarAdminException;
-
-    /**
-     * Expire all messages older than given N (expireTimeInSeconds) seconds for a given subscription asynchronously
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param expireTimeInSeconds
-     *            Expire messages older than time in seconds
-     * @return
-     */
-    public CompletableFuture<Void> expireMessagesAsync(String topic, String subscriptionName,
-            long expireTimeInSeconds);
-
-    /**
-     * Expire all messages older than given N (expireTimeInSeconds) seconds for all subscriptions of the
-     * persistent-topic
-     *
-     * @param topic
-     *            topic name
-     * @param expireTimeInSeconds
-     *            Expire messages older than time in seconds
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    public void expireMessagesForAllSubscriptions(String topic, long expireTimeInSeconds)
-            throws PulsarAdminException;
-
-    /**
-     * Expire all messages older than given N (expireTimeInSeconds) seconds for all subscriptions of the
-     * persistent-topic asynchronously
-     *
-     * @param topic
-     *            topic name
-     * @param expireTimeInSeconds
-     *            Expire messages older than time in seconds
-     */
-    public CompletableFuture<Void> expireMessagesForAllSubscriptionsAsync(String topic, long expireTimeInSeconds);
-
-    /**
-     * Peek messages from a topic subscription
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param numMessages
-     *            Number of messages
-     * @return
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic or subscription does not exist
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    List<Message<byte[]>> peekMessages(String topic, String subName, int numMessages) throws PulsarAdminException;
-
-    /**
-     * Peek messages from a topic subscription asynchronously
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param numMessages
-     *            Number of messages
-     * @return a future that can be used to track when the messages are returned
-     */
-    CompletableFuture<List<Message<byte[]>>> peekMessagesAsync(String topic, String subName, int numMessages);
-
-    /**
-     * Create a new subscription on a topic
-     *
-     * @param topic
-     *            topic name
-     * @param subscriptionName
-     *            Subscription name
-     * @param messageId
-     *            The {@link MessageId} on where to initialize the subscription. It could be {@link MessageId#latest},
-     *            {@link MessageId#earliest} or a specific message id.
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws ConflictException
-     *             Subscription already exists
-     * @throws NotAllowedException
-     *             Command disallowed for requested resource
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void createSubscription(String topic, String subscriptionName, MessageId messageId)
-            throws PulsarAdminException;
-
-    /**
-     * Create a new subscription on a topic
-     *
-     * @param topic
-     *            topic name
-     * @param subscriptionName
-     *            Subscription name
-     * @param messageId
-     *            The {@link MessageId} on where to initialize the subscription. It could be {@link MessageId#latest},
-     *            {@link MessageId#earliest} or a specific message id.
-     */
-    CompletableFuture<Void> createSubscriptionAsync(String topic, String subscriptionName, MessageId messageId);
-
-    /**
-     * Reset cursor position on a topic subscription
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param timestamp
-     *            reset subscription to position closest to time in ms since epoch
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic or subscription does not exist
-     * @throws NotAllowedException
-     *             Command disallowed for requested resource
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void resetCursor(String topic, String subName, long timestamp) throws PulsarAdminException;
-
-    /**
-     * Reset cursor position on a topic subscription
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param timestamp
-     *            reset subscription to position closest to time in ms since epoch
-     */
-    CompletableFuture<Void> resetCursorAsync(String topic, String subName, long timestamp);
-
-    /**
-     * Reset cursor position on a topic subscription
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param messageId
-     *            reset subscription to messageId (or previous nearest messageId if given messageId is not valid)
-     *
-     * @throws NotAuthorizedException
-     *             Don't have admin permission
-     * @throws NotFoundException
-     *             Topic or subscription does not exist
-     * @throws NotAllowedException
-     *             Command disallowed for requested resource
-     * @throws PulsarAdminException
-     *             Unexpected error
-     */
-    void resetCursor(String topic, String subName, MessageId messageId) throws PulsarAdminException;
-
-    /**
-     * Reset cursor position on a topic subscription
-     *
-     * @param topic
-     *            topic name
-     * @param subName
-     *            Subscription name
-     * @param MessageId
-     *            reset subscription to messageId (or previous nearest messageId if given messageId is not valid)
-     */
-    CompletableFuture<Void> resetCursorAsync(String topic, String subName, MessageId messageId);
-
-    /**
-     * Trigger compaction to run for a topic. A single topic can only have one instance of compaction
-     * running at any time. Any attempt to trigger another will be met with a ConflictException.
-     *
-     * @param topic
-     *            The topic on which to trigger compaction
-     */
-    void triggerCompaction(String topic) throws PulsarAdminException;
+/**
+ * @deprecated since 2.0. See {@link Topics}
+ */
+@Deprecated
+public interface PersistentTopics extends Topics {
 
-    /**
-     * Check the status of an ongoing compaction for a topic.
-     *
-     * @param topic The topic whose compaction status we wish to check
-     */
-    CompactionStatus compactionStatus(String topic) throws PulsarAdminException;
 }
diff --git a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PulsarAdmin.java b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PulsarAdmin.java
index f622361557..30918f9ca1 100644
--- a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PulsarAdmin.java
+++ b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PulsarAdmin.java
@@ -38,7 +38,7 @@
 import org.apache.pulsar.client.admin.internal.LookupImpl;
 import org.apache.pulsar.client.admin.internal.NamespacesImpl;
 import org.apache.pulsar.client.admin.internal.NonPersistentTopicsImpl;
-import org.apache.pulsar.client.admin.internal.PersistentTopicsImpl;
+import org.apache.pulsar.client.admin.internal.TopicsImpl;
 import org.apache.pulsar.client.admin.internal.TenantsImpl;
 import org.apache.pulsar.client.admin.internal.PulsarAdminBuilderImpl;
 import org.apache.pulsar.client.admin.internal.ResourceQuotasImpl;
@@ -71,7 +71,7 @@
     private final Tenants tenants;
     private final Properties properties;
     private final Namespaces namespaces;
-    private final PersistentTopics persistentTopics;
+    private final TopicsImpl topics;
     private final NonPersistentTopics nonPersistentTopics;
     private final ResourceQuotas resourceQuotas;
     private final ClientConfigurationData clientConfigData;
@@ -170,7 +170,7 @@ public PulsarAdmin(String serviceUrl, ClientConfigurationData clientConfigData)
         this.tenants = new TenantsImpl(root, auth);
         this.properties = new TenantsImpl(root, auth);;
         this.namespaces = new NamespacesImpl(root, auth);
-        this.persistentTopics = new PersistentTopicsImpl(root, auth);
+        this.topics = new TopicsImpl(root, auth);
         this.nonPersistentTopics = new NonPersistentTopicsImpl(root, auth);
         this.resourceQuotas = new ResourceQuotasImpl(root, auth);
         this.lookups = new LookupImpl(root, auth, useTls);
@@ -289,16 +289,24 @@ public Namespaces namespaces() {
         return namespaces;
     }
 
+    public Topics topics() {
+        return topics;
+    }
+
     /**
      * @return the persistentTopics management object
+     * @deprecated Since 2.0. See {@link #topics()}
      */
+    @Deprecated
     public PersistentTopics persistentTopics() {
-        return persistentTopics;
+        return topics;
     }
 
     /**
      * @return the persistentTopics management object
+     * @deprecated Since 2.0. See {@link #topics()}
      */
+    @Deprecated
     public NonPersistentTopics nonPersistentTopics() {
         return nonPersistentTopics;
     }
diff --git a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/Topics.java b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/Topics.java
new file mode 100644
index 0000000000..2d0d5e5dbc
--- /dev/null
+++ b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/Topics.java
@@ -0,0 +1,940 @@
+/**
+ * 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.pulsar.client.admin;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.pulsar.client.admin.PulsarAdminException.ConflictException;
+import org.apache.pulsar.client.admin.PulsarAdminException.NotAllowedException;
+import org.apache.pulsar.client.admin.PulsarAdminException.NotAuthorizedException;
+import org.apache.pulsar.client.admin.PulsarAdminException.NotFoundException;
+import org.apache.pulsar.client.admin.PulsarAdminException.PreconditionFailedException;
+import org.apache.pulsar.client.api.Message;
+import org.apache.pulsar.client.api.MessageId;
+import org.apache.pulsar.common.compaction.CompactionStatus;
+import org.apache.pulsar.common.partition.PartitionedTopicMetadata;
+import org.apache.pulsar.common.policies.data.AuthAction;
+import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
+import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
+import org.omg.CosNaming.NamingContextPackage.NotFound;
+
+import com.google.gson.JsonObject;
+
+public interface Topics {
+
+    /**
+     * Get the list of topics under a namespace.
+     * <p>
+     * Response example:
+     *
+     * <pre>
+     * <code>["topic://my-tenant/my-namespace/topic-1",
+     *  "topic://my-tenant/my-namespace/topic-2"]</code>
+     * </pre>
+     *
+     * @param namespace
+     *            Namespace name
+     * @return a list of topics
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Namespace does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    List<String> getList(String namespace) throws PulsarAdminException;
+
+    /**
+     * Get the list of partitioned topics under a namespace.
+     * <p>
+     * Response example:
+     *
+     * <pre>
+     * <code>["persistent://my-tenant/my-namespace/topic-1",
+     *  "persistent://my-tenant/my-namespace/topic-2"]</code>
+     * </pre>
+     *
+     * @param namespace
+     *            Namespace name
+     * @return a list of partitioned topics
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Namespace does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    List<String> getPartitionedTopicList(String namespace) throws PulsarAdminException;
+
+    /**
+     * Get list of topics exist into given bundle
+     *
+     * @param namespace
+     * @param bundleRange
+     * @return
+     * @throws PulsarAdminException
+     */
+    List<String> getListInBundle(String namespace, String bundleRange)
+            throws PulsarAdminException;
+
+    /**
+     * Get list of topics exist into given bundle asynchronously.
+     *
+     * @param namespace
+     * @param bundleRange
+     * @return
+     */
+    CompletableFuture<List<String>> getListInBundleAsync(String namespace, String bundleRange);
+
+    /**
+     * Get permissions on a topic.
+     * <p>
+     * Retrieve the effective permissions for a topic. These permissions are defined by the permissions set at the
+     * namespace level combined (union) with any eventual specific permission set on the topic.
+     * <p>
+     * Response Example:
+     *
+     * <pre>
+     * <code>{
+     *   "role-1" : [ "produce" ],
+     *   "role-2" : [ "consume" ]
+     * }</code>
+     * </pre>
+     *
+     * @param topic
+     *            Topic url
+     * @return a map of topics an their permissions set
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Namespace does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    Map<String, Set<AuthAction>> getPermissions(String topic) throws PulsarAdminException;
+
+    /**
+     * Grant permission on a topic.
+     * <p>
+     * Grant a new permission to a client role on a single topic.
+     * <p>
+     * Request parameter example:
+     *
+     * <pre>
+     * <code>["produce", "consume"]</code>
+     * </pre>
+     *
+     * @param topic
+     *            Topic url
+     * @param role
+     *            Client role to which grant permission
+     * @param actions
+     *            Auth actions (produce and consume)
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Namespace does not exist
+     * @throws ConflictException
+     *             Concurrent modification
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void grantPermission(String topic, String role, Set<AuthAction> actions) throws PulsarAdminException;
+
+    /**
+     * Revoke permissions on a topic.
+     * <p>
+     * Revoke permissions to a client role on a single topic. If the permission was not set at the topic level, but
+     * rather at the namespace level, this operation will return an error (HTTP status code 412).
+     *
+     * @param topic
+     *            Topic url
+     * @param role
+     *            Client role to which remove permission
+     * @throws UniformInterfaceException
+     *             if the operations was not successful
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFound
+     *             Namespace does not exist
+     * @throws PreconditionFailedException
+     *             Permissions are not set at the topic level
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void revokePermissions(String topic, String role) throws PulsarAdminException;
+
+    /**
+     * Create a partitioned topic.
+     * <p>
+     * Create a partitioned topic. It needs to be called before creating a producer for a partitioned topic.
+     * <p>
+     *
+     * @param topic
+     *            Topic name
+     * @param numPartitions
+     *            Number of partitions to create of the topic
+     * @throws PulsarAdminException
+     */
+    void createPartitionedTopic(String topic, int numPartitions) throws PulsarAdminException;
+
+    /**
+     * Create a partitioned topic asynchronously.
+     * <p>
+     * Create a partitioned topic asynchronously. It needs to be called before creating a producer for a partitioned
+     * topic.
+     * <p>
+     *
+     * @param topic
+     *            Topic name
+     * @param numPartitions
+     *            Number of partitions to create of the topic
+     * @return a future that can be used to track when the partitioned topic is created
+     */
+    CompletableFuture<Void> createPartitionedTopicAsync(String topic, int numPartitions);
+
+    /**
+     * Update number of partitions of a non-global partitioned topic.
+     * <p>
+     * It requires partitioned-topic to be already exist and number of new partitions must be greater than existing
+     * number of partitions. Decrementing number of partitions requires deletion of topic which is not supported.
+     * <p>
+     *
+     * @param topic
+     *            Topic name
+     * @param numPartitions
+     *            Number of new partitions of already exist partitioned-topic
+     *
+     * @return a future that can be used to track when the partitioned topic is updated
+     */
+    void updatePartitionedTopic(String topic, int numPartitions) throws PulsarAdminException;
+
+    /**
+     * Update number of partitions of a non-global partitioned topic asynchronously.
+     * <p>
+     * It requires partitioned-topic to be already exist and number of new partitions must be greater than existing
+     * number of partitions. Decrementing number of partitions requires deletion of topic which is not supported.
+     * <p>
+     *
+     * @param topic
+     *            Topic name
+     * @param numPartitions
+     *            Number of new partitions of already exist partitioned-topic
+     *
+     * @return a future that can be used to track when the partitioned topic is updated
+     */
+    CompletableFuture<Void> updatePartitionedTopicAsync(String topic, int numPartitions);
+
+    /**
+     * Get metadata of a partitioned topic.
+     * <p>
+     * Get metadata of a partitioned topic.
+     * <p>
+     *
+     * @param topic
+     *            Topic name
+     * @return Partitioned topic metadata
+     * @throws PulsarAdminException
+     */
+    PartitionedTopicMetadata getPartitionedTopicMetadata(String topic) throws PulsarAdminException;
+
+    /**
+     * Get metadata of a partitioned topic asynchronously.
+     * <p>
+     * Get metadata of a partitioned topic asynchronously.
+     * <p>
+     *
+     * @param topic
+     *            Topic name
+     * @return a future that can be used to track when the partitioned topic metadata is returned
+     */
+    CompletableFuture<PartitionedTopicMetadata> getPartitionedTopicMetadataAsync(String topic);
+
+    /**
+     * Delete a partitioned topic.
+     * <p>
+     * It will also delete all the partitions of the topic if it exists.
+     * <p>
+     *
+     * @param topic
+     *            Topic name
+     * @throws PulsarAdminException
+     */
+    void deletePartitionedTopic(String topic) throws PulsarAdminException;
+
+    /**
+     * Delete a partitioned topic asynchronously.
+     * <p>
+     * It will also delete all the partitions of the topic if it exists.
+     * <p>
+     *
+     * @param topic
+     *            Topic name
+     * @return a future that can be used to track when the partitioned topic is deleted
+     */
+    CompletableFuture<Void> deletePartitionedTopicAsync(String topic);
+
+    /**
+     * Delete a topic.
+     * <p>
+     * Delete a topic. The topic cannot be deleted if there's any active subscription or producer connected to the it.
+     * <p>
+     *
+     * @param topic
+     *            Topic name
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic does not exist
+     * @throws PreconditionFailedException
+     *             Topic has active subscriptions or producers
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void delete(String topic) throws PulsarAdminException;
+
+    /**
+     * Delete a topic asynchronously.
+     * <p>
+     * Delete a topic asynchronously. The topic cannot be deleted if there's any active subscription or producer
+     * connected to the it.
+     * <p>
+     *
+     * @param topic
+     *            topic name
+     *
+     * @return a future that can be used to track when the topic is deleted
+     */
+    CompletableFuture<Void> deleteAsync(String topic);
+
+    /**
+     * Unload a topic.
+     * <p>
+     *
+     * @param topic
+     *            topic name
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             topic does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void unload(String topic) throws PulsarAdminException;
+
+    /**
+     * Unload a topic asynchronously.
+     * <p>
+     *
+     * @param topic
+     *            topic name
+     *
+     * @return a future that can be used to track when the topic is unloaded
+     */
+    CompletableFuture<Void> unloadAsync(String topic);
+
+    /**
+     * Terminate the topic and prevent any more messages being published on it.
+     * <p>
+     * This
+     *
+     * @param topic
+     *            topic name
+     * @return the message id of the last message that was published in the topic
+     */
+    CompletableFuture<MessageId> terminateTopicAsync(String topic);
+
+    /**
+     * Get the list of subscriptions.
+     * <p>
+     * Get the list of persistent subscriptions for a given topic.
+     * <p>
+     *
+     * @param topic
+     *            topic name
+     * @return the list of subscriptions
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    List<String> getSubscriptions(String topic) throws PulsarAdminException;
+
+    /**
+     * Get the list of subscriptions asynchronously.
+     * <p>
+     * Get the list of persistent subscriptions for a given topic.
+     * <p>
+     *
+     * @param topic
+     *            topic name
+     * @return a future that can be used to track when the list of subscriptions is returned
+     */
+    CompletableFuture<List<String>> getSubscriptionsAsync(String topic);
+
+    /**
+     * Get the stats for the topic.
+     * <p>
+     * Response Example:
+     *
+     * <pre>
+     * <code>
+     * {
+     *   "msgRateIn" : 100.0,                    // Total rate of messages published on the topic. msg/s
+     *   "msgThroughputIn" : 10240.0,            // Total throughput of messages published on the topic. byte/s
+     *   "msgRateOut" : 100.0,                   // Total rate of messages delivered on the topic. msg/s
+     *   "msgThroughputOut" : 10240.0,           // Total throughput of messages delivered on the topic. byte/s
+     *   "averageMsgSize" : 1024.0,              // Average size of published messages. bytes
+     *   "publishers" : [                        // List of publishes on this topic with their stats
+     *      {
+     *          "producerId" : 10                // producer id
+     *          "address"   : 10.4.1.23:3425     // IP and port for this producer
+     *          "connectedSince" : 2014-11-21 23:54:46 // Timestamp of this published connection
+     *          "msgRateIn" : 100.0,             // Total rate of messages published by this producer. msg/s
+     *          "msgThroughputIn" : 10240.0,     // Total throughput of messages published by this producer. byte/s
+     *          "averageMsgSize" : 1024.0,       // Average size of published messages by this producer. bytes
+     *      },
+     *   ],
+     *   "subscriptions" : {                     // Map of subscriptions on this topic
+     *     "sub1" : {
+     *       "msgRateOut" : 100.0,               // Total rate of messages delivered on this subscription. msg/s
+     *       "msgThroughputOut" : 10240.0,       // Total throughput delivered on this subscription. bytes/s
+     *       "msgBacklog" : 0,                   // Number of messages in the subscriotion backlog
+     *       "type" : Exclusive                  // Whether the subscription is exclusive or shared
+     *       "consumers" [                       // List of consumers on this subscription
+     *          {
+     *              "id" : 5                            // Consumer id
+     *              "address" : 10.4.1.23:3425          // IP and port for this consumer
+     *              "connectedSince" : 2014-11-21 23:54:46 // Timestamp of this consumer connection
+     *              "msgRateOut" : 100.0,               // Total rate of messages delivered to this consumer. msg/s
+     *              "msgThroughputOut" : 10240.0,       // Total throughput delivered to this consumer. bytes/s
+     *          }
+     *       ],
+     *   },
+     *   "replication" : {                    // Replication statistics
+     *     "cluster_1" : {                    // Cluster name in the context of from-cluster or to-cluster
+     *       "msgRateIn" : 100.0,             // Total rate of messages received from this remote cluster. msg/s
+     *       "msgThroughputIn" : 10240.0,     // Total throughput received from this remote cluster. bytes/s
+     *       "msgRateOut" : 100.0,            // Total rate of messages delivered to the replication-subscriber. msg/s
+     *       "msgThroughputOut" : 10240.0,    // Total throughput delivered to the replication-subscriber. bytes/s
+     *       "replicationBacklog" : 0,        // Number of messages pending to be replicated to this remote cluster
+     *       "connected" : true,              // Whether the replication-subscriber is currently connected locally
+     *     },
+     *     "cluster_2" : {
+     *       "msgRateIn" : 100.0,
+     *       "msgThroughputIn" : 10240.0,
+     *       "msgRateOut" : 100.0,
+     *       "msghroughputOut" : 10240.0,
+     *       "replicationBacklog" : 0,
+     *       "connected" : true,
+     *     }
+     *   },
+     * }
+     * </code>
+     * </pre>
+     *
+     * All the rates are computed over a 1 minute window and are relative the last completed 1 minute period.
+     *
+     * @param topic
+     *            topic name
+     * @return the topic statistics
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    TopicStats getStats(String topic) throws PulsarAdminException;
+
+    /**
+     * Get the stats for the topic asynchronously. All the rates are computed over a 1 minute window and are relative
+     * the last completed 1 minute period.
+     *
+     * @param topic
+     *            topic name
+     *
+     * @return a future that can be used to track when the topic statistics are returned
+     *
+     */
+    CompletableFuture<TopicStats> getStatsAsync(String topic);
+
+    /**
+     * Get the internal stats for the topic.
+     * <p>
+     * Access the internal state of the topic
+     *
+     * @param topic
+     *            topic name
+     * @return the topic statistics
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    PersistentTopicInternalStats getInternalStats(String topic) throws PulsarAdminException;
+
+    /**
+     * Get the internal stats for the topic asynchronously.
+     *
+     * @param topic
+     *            topic Name
+     *
+     * @return a future that can be used to track when the internal topic statistics are returned
+     */
+    CompletableFuture<PersistentTopicInternalStats> getInternalStatsAsync(String topic);
+
+    /**
+     * Get a JSON representation of the topic metadata stored in ZooKeeper
+     *
+     * @param topic
+     *            topic name
+     * @return the topic internal metadata
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    JsonObject getInternalInfo(String topic) throws PulsarAdminException;
+
+    /**
+     * Get a JSON representation of the topic metadata stored in ZooKeeper
+     *
+     * @param topic
+     *            topic name
+     * @return a future to receive the topic internal metadata
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    CompletableFuture<JsonObject> getInternalInfoAsync(String topic);
+
+    /**
+     * Get the stats for the partitioned topic
+     * <p>
+     * Response Example:
+     *
+     * <pre>
+     * <code>
+     * {
+     *   "msgRateIn" : 100.0,                    // Total rate of messages published on the partitioned topic. msg/s
+     *   "msgThroughputIn" : 10240.0,            // Total throughput of messages published on the partitioned topic. byte/s
+     *   "msgRateOut" : 100.0,                   // Total rate of messages delivered on the partitioned topic. msg/s
+     *   "msgThroughputOut" : 10240.0,           // Total throughput of messages delivered on the partitioned topic. byte/s
+     *   "averageMsgSize" : 1024.0,              // Average size of published messages. bytes
+     *   "publishers" : [                        // List of publishes on this partitioned topic with their stats
+     *      {
+     *          "msgRateIn" : 100.0,             // Total rate of messages published by this producer. msg/s
+     *          "msgThroughputIn" : 10240.0,     // Total throughput of messages published by this producer. byte/s
+     *          "averageMsgSize" : 1024.0,       // Average size of published messages by this producer. bytes
+     *      },
+     *   ],
+     *   "subscriptions" : {                     // Map of subscriptions on this topic
+     *     "sub1" : {
+     *       "msgRateOut" : 100.0,               // Total rate of messages delivered on this subscription. msg/s
+     *       "msgThroughputOut" : 10240.0,       // Total throughput delivered on this subscription. bytes/s
+     *       "msgBacklog" : 0,                   // Number of messages in the subscriotion backlog
+     *       "type" : Exclusive                  // Whether the subscription is exclusive or shared
+     *       "consumers" [                       // List of consumers on this subscription
+     *          {
+     *              "msgRateOut" : 100.0,               // Total rate of messages delivered to this consumer. msg/s
+     *              "msgThroughputOut" : 10240.0,       // Total throughput delivered to this consumer. bytes/s
+     *          }
+     *       ],
+     *   },
+     *   "replication" : {                    // Replication statistics
+     *     "cluster_1" : {                    // Cluster name in the context of from-cluster or to-cluster
+     *       "msgRateIn" : 100.0,             // Total rate of messages received from this remote cluster. msg/s
+     *       "msgThroughputIn" : 10240.0,     // Total throughput received from this remote cluster. bytes/s
+     *       "msgRateOut" : 100.0,            // Total rate of messages delivered to the replication-subscriber. msg/s
+     *       "msgThroughputOut" : 10240.0,    // Total throughput delivered to the replication-subscriber. bytes/s
+     *       "replicationBacklog" : 0,        // Number of messages pending to be replicated to this remote cluster
+     *       "connected" : true,              // Whether the replication-subscriber is currently connected locally
+     *     },
+     *     "cluster_2" : {
+     *       "msgRateIn" : 100.0,
+     *       "msgThroughputIn" : 10240.0,
+     *       "msgRateOut" : 100.0,
+     *       "msghroughputOut" : 10240.0,
+     *       "replicationBacklog" : 0,
+     *       "connected" : true,
+     *     }
+     *   },
+     * }
+     * </code>
+     * </pre>
+     *
+     * All the rates are computed over a 1 minute window and are relative the last completed 1 minute period.
+     *
+     * @param topic
+     *            topic name
+     * @param perPartition
+     *
+     * @return the partitioned topic statistics
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     *
+     */
+    PartitionedTopicStats getPartitionedStats(String topic, boolean perPartition) throws PulsarAdminException;
+
+    /**
+     * Get the stats for the partitioned topic asynchronously
+     *
+     * @param topic
+     *            topic Name
+     * @param perPartition
+     *            flag to get stats per partition
+     * @return a future that can be used to track when the partitioned topic statistics are returned
+     */
+    CompletableFuture<PartitionedTopicStats> getPartitionedStatsAsync(String topic, boolean perPartition);
+
+    /**
+     * Delete a subscription.
+     * <p>
+     * Delete a persistent subscription from a topic. There should not be any active consumers on the subscription.
+     * <p>
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic or subscription does not exist
+     * @throws PreconditionFailedException
+     *             Subscription has active consumers
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void deleteSubscription(String topic, String subName) throws PulsarAdminException;
+
+    /**
+     * Delete a subscription asynchronously.
+     * <p>
+     * Delete a persistent subscription from a topic. There should not be any active consumers on the subscription.
+     * <p>
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     *
+     * @return a future that can be used to track when the subscription is deleted
+     */
+    CompletableFuture<Void> deleteSubscriptionAsync(String topic, String subName);
+
+    /**
+     * Skip all messages on a topic subscription.
+     * <p>
+     * Completely clears the backlog on the subscription.
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic or subscription does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void skipAllMessages(String topic, String subName) throws PulsarAdminException;
+
+    /**
+     * Skip all messages on a topic subscription asynchronously.
+     * <p>
+     * Completely clears the backlog on the subscription.
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     *
+     * @return a future that can be used to track when all the messages are skipped
+     */
+    CompletableFuture<Void> skipAllMessagesAsync(String topic, String subName);
+
+    /**
+     * Skip messages on a topic subscription.
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param numMessages
+     *            Number of messages
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic or subscription does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void skipMessages(String topic, String subName, long numMessages) throws PulsarAdminException;
+
+    /**
+     * Skip messages on a topic subscription asynchronously.
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param numMessages
+     *            Number of messages
+     *
+     * @return a future that can be used to track when the number of messages are skipped
+     */
+    CompletableFuture<Void> skipMessagesAsync(String topic, String subName, long numMessages);
+
+    /**
+     * Expire all messages older than given N (expireTimeInSeconds) seconds for a given subscription
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param expireTimeInSeconds
+     *            Expire messages older than time in seconds
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    public void expireMessages(String topic, String subscriptionName, long expireTimeInSeconds)
+            throws PulsarAdminException;
+
+    /**
+     * Expire all messages older than given N (expireTimeInSeconds) seconds for a given subscription asynchronously
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param expireTimeInSeconds
+     *            Expire messages older than time in seconds
+     * @return
+     */
+    public CompletableFuture<Void> expireMessagesAsync(String topic, String subscriptionName,
+            long expireTimeInSeconds);
+
+    /**
+     * Expire all messages older than given N (expireTimeInSeconds) seconds for all subscriptions of the
+     * persistent-topic
+     *
+     * @param topic
+     *            topic name
+     * @param expireTimeInSeconds
+     *            Expire messages older than time in seconds
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    public void expireMessagesForAllSubscriptions(String topic, long expireTimeInSeconds)
+            throws PulsarAdminException;
+
+    /**
+     * Expire all messages older than given N (expireTimeInSeconds) seconds for all subscriptions of the
+     * persistent-topic asynchronously
+     *
+     * @param topic
+     *            topic name
+     * @param expireTimeInSeconds
+     *            Expire messages older than time in seconds
+     */
+    public CompletableFuture<Void> expireMessagesForAllSubscriptionsAsync(String topic, long expireTimeInSeconds);
+
+    /**
+     * Peek messages from a topic subscription
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param numMessages
+     *            Number of messages
+     * @return
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic or subscription does not exist
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    List<Message<byte[]>> peekMessages(String topic, String subName, int numMessages) throws PulsarAdminException;
+
+    /**
+     * Peek messages from a topic subscription asynchronously
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param numMessages
+     *            Number of messages
+     * @return a future that can be used to track when the messages are returned
+     */
+    CompletableFuture<List<Message<byte[]>>> peekMessagesAsync(String topic, String subName, int numMessages);
+
+    /**
+     * Create a new subscription on a topic
+     *
+     * @param topic
+     *            topic name
+     * @param subscriptionName
+     *            Subscription name
+     * @param messageId
+     *            The {@link MessageId} on where to initialize the subscription. It could be {@link MessageId#latest},
+     *            {@link MessageId#earliest} or a specific message id.
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws ConflictException
+     *             Subscription already exists
+     * @throws NotAllowedException
+     *             Command disallowed for requested resource
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void createSubscription(String topic, String subscriptionName, MessageId messageId)
+            throws PulsarAdminException;
+
+    /**
+     * Create a new subscription on a topic
+     *
+     * @param topic
+     *            topic name
+     * @param subscriptionName
+     *            Subscription name
+     * @param messageId
+     *            The {@link MessageId} on where to initialize the subscription. It could be {@link MessageId#latest},
+     *            {@link MessageId#earliest} or a specific message id.
+     */
+    CompletableFuture<Void> createSubscriptionAsync(String topic, String subscriptionName, MessageId messageId);
+
+    /**
+     * Reset cursor position on a topic subscription
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param timestamp
+     *            reset subscription to position closest to time in ms since epoch
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic or subscription does not exist
+     * @throws NotAllowedException
+     *             Command disallowed for requested resource
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void resetCursor(String topic, String subName, long timestamp) throws PulsarAdminException;
+
+    /**
+     * Reset cursor position on a topic subscription
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param timestamp
+     *            reset subscription to position closest to time in ms since epoch
+     */
+    CompletableFuture<Void> resetCursorAsync(String topic, String subName, long timestamp);
+
+    /**
+     * Reset cursor position on a topic subscription
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param messageId
+     *            reset subscription to messageId (or previous nearest messageId if given messageId is not valid)
+     *
+     * @throws NotAuthorizedException
+     *             Don't have admin permission
+     * @throws NotFoundException
+     *             Topic or subscription does not exist
+     * @throws NotAllowedException
+     *             Command disallowed for requested resource
+     * @throws PulsarAdminException
+     *             Unexpected error
+     */
+    void resetCursor(String topic, String subName, MessageId messageId) throws PulsarAdminException;
+
+    /**
+     * Reset cursor position on a topic subscription
+     *
+     * @param topic
+     *            topic name
+     * @param subName
+     *            Subscription name
+     * @param MessageId
+     *            reset subscription to messageId (or previous nearest messageId if given messageId is not valid)
+     */
+    CompletableFuture<Void> resetCursorAsync(String topic, String subName, MessageId messageId);
+
+    /**
+     * Trigger compaction to run for a topic. A single topic can only have one instance of compaction
+     * running at any time. Any attempt to trigger another will be met with a ConflictException.
+     *
+     * @param topic
+     *            The topic on which to trigger compaction
+     */
+    void triggerCompaction(String topic) throws PulsarAdminException;
+
+    /**
+     * Check the status of an ongoing compaction for a topic.
+     *
+     * @param topic The topic whose compaction status we wish to check
+     */
+    CompactionStatus compactionStatus(String topic) throws PulsarAdminException;
+}
diff --git a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/NonPersistentTopicsImpl.java b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/NonPersistentTopicsImpl.java
index ccd712a72c..ddc4c1b84d 100644
--- a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/NonPersistentTopicsImpl.java
+++ b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/NonPersistentTopicsImpl.java
@@ -45,8 +45,8 @@
 
     public NonPersistentTopicsImpl(WebTarget web, Authentication auth) {
         super(auth);
-        adminNonPersistentTopics = web.path("/admin/non-persistent");
-        adminV2NonPersistentTopics = web.path("/admin/v2/non-persistent");
+        adminNonPersistentTopics = web.path("/admin");
+        adminV2NonPersistentTopics = web.path("/admin/v2");
     }
 
     @Override
@@ -203,7 +203,7 @@ public void unload(String topic) throws PulsarAdminException {
     public CompletableFuture<List<String>> getListInBundleAsync(String namespace, String bundleRange) {
         NamespaceName ns = NamespaceName.get(namespace);
         final CompletableFuture<List<String>> future = new CompletableFuture<>();
-        WebTarget path = namespacePath(ns, bundleRange);
+        WebTarget path = namespacePath("non-persistent", ns, bundleRange);
         asyncGetRequest(path,
                 new InvocationCallback<List<String>>() {
                     @Override
@@ -234,7 +234,7 @@ public void failed(Throwable throwable) {
     public CompletableFuture<List<String>> getListAsync(String namespace) {
         NamespaceName ns = NamespaceName.get(namespace);
         final CompletableFuture<List<String>> future = new CompletableFuture<>();
-        WebTarget path = namespacePath(ns);
+        WebTarget path = namespacePath("non-persistent", ns);
         asyncGetRequest(path,
                 new InvocationCallback<List<String>>() {
                     @Override
@@ -258,9 +258,9 @@ private TopicName validateTopic(String topic) {
         return TopicName.get(topic);
     }
 
-    private WebTarget namespacePath(NamespaceName namespace, String... parts) {
+    private WebTarget namespacePath(String domain, NamespaceName namespace, String... parts) {
         final WebTarget base = namespace.isV2() ? adminV2NonPersistentTopics : adminNonPersistentTopics;
-        WebTarget namespacePath = base.path(namespace.toString());
+        WebTarget namespacePath = base.path(domain).path(namespace.toString());
         namespacePath = WebTargets.addParts(namespacePath, parts);
         return namespacePath;
     }
diff --git a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/PersistentTopicsImpl.java b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/TopicsImpl.java
similarity index 89%
rename from pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/PersistentTopicsImpl.java
rename to pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/TopicsImpl.java
index 86f2c05b98..8749d6ae6f 100644
--- a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/PersistentTopicsImpl.java
+++ b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/TopicsImpl.java
@@ -20,6 +20,14 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -29,6 +37,8 @@
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import javax.ws.rs.ClientErrorException;
 import javax.ws.rs.ServerErrorException;
@@ -45,6 +55,7 @@
 import org.apache.pulsar.client.admin.PersistentTopics;
 import org.apache.pulsar.client.admin.PulsarAdminException;
 import org.apache.pulsar.client.admin.PulsarAdminException.NotFoundException;
+import org.apache.pulsar.client.admin.Topics;
 import org.apache.pulsar.client.api.Authentication;
 import org.apache.pulsar.client.api.Message;
 import org.apache.pulsar.client.api.MessageId;
@@ -56,42 +67,43 @@
 import org.apache.pulsar.common.api.proto.PulsarApi.KeyValue;
 import org.apache.pulsar.common.api.proto.PulsarApi.SingleMessageMetadata;
 import org.apache.pulsar.common.compaction.CompactionStatus;
-import org.apache.pulsar.common.naming.TopicName;
 import org.apache.pulsar.common.naming.NamespaceName;
+import org.apache.pulsar.common.naming.TopicName;
 import org.apache.pulsar.common.partition.PartitionedTopicMetadata;
 import org.apache.pulsar.common.policies.data.AuthAction;
 import org.apache.pulsar.common.policies.data.ErrorData;
 import org.apache.pulsar.common.policies.data.PartitionedTopicStats;
 import org.apache.pulsar.common.policies.data.PersistentTopicInternalStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.util.Codec;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-
-public class PersistentTopicsImpl extends BaseResource implements PersistentTopics {
-    private final WebTarget adminPersistentTopics;
-    private final WebTarget adminV2PersistentTopics;
+@SuppressWarnings("deprecation")
+public class TopicsImpl extends BaseResource implements Topics, PersistentTopics {
+    private final WebTarget adminTopics;
+    private final WebTarget adminV2Topics;
     private final String BATCH_HEADER = "X-Pulsar-num-batch-message";
-    public PersistentTopicsImpl(WebTarget web, Authentication auth) {
+    public TopicsImpl(WebTarget web, Authentication auth) {
         super(auth);
-        adminPersistentTopics = web.path("/admin/persistent");
-        adminV2PersistentTopics = web.path("/admin/v2/persistent");
+        adminTopics = web.path("/admin");
+        adminV2Topics = web.path("/admin/v2");
     }
 
     @Override
     public List<String> getList(String namespace) throws PulsarAdminException {
         try {
             NamespaceName ns = NamespaceName.get(namespace);
-            WebTarget path = namespacePath(ns);
-            return request(path).get(new GenericType<List<String>>() {});
+            WebTarget persistentPath = namespacePath("persistent", ns);
+            WebTarget nonPersistentPath = namespacePath("non-persistent", ns);
+
+            List<String> persistentTopics = request(persistentPath).get(new GenericType<List<String>>() {
+            });
+
+            List<String> nonPersistentTopics = request(nonPersistentPath).get(new GenericType<List<String>>() {
+            });
+            return new ArrayList<>(
+                    Stream.concat(persistentTopics.stream(), nonPersistentTopics.stream()).collect(Collectors.toSet()));
         } catch (Exception e) {
             throw getApiException(e);
         }
@@ -101,13 +113,55 @@ public PersistentTopicsImpl(WebTarget web, Authentication auth) {
     public List<String> getPartitionedTopicList(String namespace) throws PulsarAdminException {
         try {
             NamespaceName ns = NamespaceName.get(namespace);
-            WebTarget path = namespacePath(ns, "partitioned");
-            return request(path).get(new GenericType<List<String>>() {});
+
+            WebTarget persistentPath = namespacePath("persistent", ns, "partitioned");
+            WebTarget nonPersistentPath = namespacePath("non-persistent", ns, "partitioned");
+            List<String> persistentTopics = request(persistentPath).get(new GenericType<List<String>>() {
+            });
+
+            List<String> nonPersistentTopics = request(nonPersistentPath).get(new GenericType<List<String>>() {
+            });
+            return new ArrayList<>(
+                    Stream.concat(persistentTopics.stream(), nonPersistentTopics.stream()).collect(Collectors.toSet()));
         } catch (Exception e) {
             throw getApiException(e);
         }
     }
 
+
+    @Override
+    public List<String> getListInBundle(String namespace, String bundleRange) throws PulsarAdminException {
+        try {
+            return getListInBundleAsync(namespace, bundleRange).get();
+        } catch (ExecutionException e) {
+            throw (PulsarAdminException) e.getCause();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new PulsarAdminException(e.getCause());
+        }
+    }
+
+    @Override
+    public CompletableFuture<List<String>> getListInBundleAsync(String namespace, String bundleRange) {
+        NamespaceName ns = NamespaceName.get(namespace);
+        final CompletableFuture<List<String>> future = new CompletableFuture<>();
+        WebTarget path = namespacePath("non-persistent", ns, bundleRange);
+
+        asyncGetRequest(path,
+                new InvocationCallback<List<String>>() {
+                    @Override
+                    public void completed(List<String> response) {
+                        future.complete(response);
+                    }
+                    @Override
+                    public void failed(Throwable throwable) {
+                        future.completeExceptionally(getApiException(throwable.getCause()));
+                    }
+                });
+        return future;
+    }
+
+
     @Override
     public Map<String, Set<AuthAction>> getPermissions(String topic) throws PulsarAdminException {
         try {
@@ -305,7 +359,7 @@ public void failed(Throwable throwable) {
     }
 
     @Override
-    public PersistentTopicStats getStats(String topic) throws PulsarAdminException {
+    public TopicStats getStats(String topic) throws PulsarAdminException {
         try {
             return getStatsAsync(topic).get();
         } catch (ExecutionException e) {
@@ -317,15 +371,15 @@ public PersistentTopicStats getStats(String topic) throws PulsarAdminException {
     }
 
     @Override
-    public CompletableFuture<PersistentTopicStats> getStatsAsync(String topic) {
+    public CompletableFuture<TopicStats> getStatsAsync(String topic) {
         TopicName tn = validateTopic(topic);
         WebTarget path = topicPath(tn, "stats");
-        final CompletableFuture<PersistentTopicStats> future = new CompletableFuture<>();
+        final CompletableFuture<TopicStats> future = new CompletableFuture<>();
         asyncGetRequest(path,
-                new InvocationCallback<PersistentTopicStats>() {
+                new InvocationCallback<TopicStats>() {
 
                     @Override
-                    public void completed(PersistentTopicStats response) {
+                    public void completed(TopicStats response) {
                         future.complete(response);
                     }
 
@@ -737,15 +791,15 @@ public CompactionStatus compactionStatus(String topic)
         }
     }
 
-    private WebTarget namespacePath(NamespaceName namespace, String... parts) {
-        final WebTarget base = namespace.isV2() ? adminV2PersistentTopics : adminPersistentTopics;
-        WebTarget namespacePath = base.path(namespace.toString());
+    private WebTarget namespacePath(String domain, NamespaceName namespace, String... parts) {
+        final WebTarget base = namespace.isV2() ? adminV2Topics : adminTopics;
+        WebTarget namespacePath = base.path(domain).path(namespace.toString());
         namespacePath = WebTargets.addParts(namespacePath, parts);
         return namespacePath;
     }
 
     private WebTarget topicPath(TopicName topic, String... parts) {
-        final WebTarget base = topic.isV2() ? adminV2PersistentTopics : adminPersistentTopics;
+        final WebTarget base = topic.isV2() ? adminV2Topics : adminTopics;
         WebTarget topicPath = base.path(topic.getRestPath());
         topicPath = WebTargets.addParts(topicPath, parts);
         return topicPath;
@@ -832,5 +886,5 @@ private TopicName validateTopic(String topic) {
         return ret;
     }
 
-    private static final Logger log = LoggerFactory.getLogger(PersistentTopicsImpl.class);
+    private static final Logger log = LoggerFactory.getLogger(TopicsImpl.class);
 }
diff --git a/pulsar-client-kafka-compat/pulsar-client-kafka/src/test/java/org/apache/pulsar/client/kafka/compat/tests/KafkaConsumerTest.java b/pulsar-client-kafka-compat/pulsar-client-kafka/src/test/java/org/apache/pulsar/client/kafka/compat/tests/KafkaConsumerTest.java
index cc56618550..5714dc5110 100644
--- a/pulsar-client-kafka-compat/pulsar-client-kafka/src/test/java/org/apache/pulsar/client/kafka/compat/tests/KafkaConsumerTest.java
+++ b/pulsar-client-kafka-compat/pulsar-client-kafka/src/test/java/org/apache/pulsar/client/kafka/compat/tests/KafkaConsumerTest.java
@@ -195,7 +195,7 @@ public void testPartitions() throws Exception {
 
         // Create 8 partitions in topic
         admin.tenants().createTenant("sample", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic(topic, 8);
+        admin.topics().createPartitionedTopic(topic, 8);
 
         Properties props = new Properties();
         props.put("bootstrap.servers", lookupUrl.toString());
diff --git a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/PulsarAdminToolTest.java b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/PulsarAdminToolTest.java
index 3bebaae8b7..9f10be3556 100644
--- a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/PulsarAdminToolTest.java
+++ b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/admin/cli/PulsarAdminToolTest.java
@@ -23,6 +23,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
 import java.util.EnumSet;
 
 import org.apache.pulsar.client.admin.BrokerStats;
@@ -35,6 +38,7 @@
 import org.apache.pulsar.client.admin.PulsarAdmin;
 import org.apache.pulsar.client.admin.ResourceQuotas;
 import org.apache.pulsar.client.admin.Tenants;
+import org.apache.pulsar.client.admin.Topics;
 import org.apache.pulsar.client.api.MessageId;
 import org.apache.pulsar.common.policies.data.AuthAction;
 import org.apache.pulsar.common.policies.data.BacklogQuota;
@@ -52,9 +56,6 @@
 import org.mockito.Mockito;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
 @Test
 public class PulsarAdminToolTest {
 
@@ -495,6 +496,89 @@ void namespaceIsolationPolicy() throws Exception {
         verify(mockClusters).getBrokerWithNamespaceIsolationPolicy("use", "my-broker");
     }
 
+    @Test
+    void topics() throws Exception {
+        PulsarAdmin admin = Mockito.mock(PulsarAdmin.class);
+        Topics mockTopics = mock(Topics.class);
+        when(admin.topics()).thenReturn(mockTopics);
+
+        CmdTopics cmdTopics = new CmdTopics(admin);
+
+        cmdTopics.run(split("delete persistent://myprop/clust/ns1/ds1"));
+        verify(mockTopics).delete("persistent://myprop/clust/ns1/ds1");
+
+        cmdTopics.run(split("unload persistent://myprop/clust/ns1/ds1"));
+        verify(mockTopics).unload("persistent://myprop/clust/ns1/ds1");
+
+        cmdTopics.run(split("list myprop/clust/ns1"));
+        verify(mockTopics).getList("myprop/clust/ns1");
+
+        cmdTopics.run(split("subscriptions persistent://myprop/clust/ns1/ds1"));
+        verify(mockTopics).getSubscriptions("persistent://myprop/clust/ns1/ds1");
+
+        cmdTopics.run(split("unsubscribe persistent://myprop/clust/ns1/ds1 -s sub1"));
+        verify(mockTopics).deleteSubscription("persistent://myprop/clust/ns1/ds1", "sub1");
+
+        cmdTopics.run(split("stats persistent://myprop/clust/ns1/ds1"));
+        verify(mockTopics).getStats("persistent://myprop/clust/ns1/ds1");
+
+        cmdTopics.run(split("stats-internal persistent://myprop/clust/ns1/ds1"));
+        verify(mockTopics).getInternalStats("persistent://myprop/clust/ns1/ds1");
+
+        cmdTopics.run(split("info-internal persistent://myprop/clust/ns1/ds1"));
+        verify(mockTopics).getInternalInfo("persistent://myprop/clust/ns1/ds1");
+
+        cmdTopics.run(split("partitioned-stats persistent://myprop/clust/ns1/ds1 --per-partition"));
+        verify(mockTopics).getPartitionedStats("persistent://myprop/clust/ns1/ds1", true);
+
+        cmdTopics.run(split("clear-backlog persistent://myprop/clust/ns1/ds1 -s sub1"));
+        verify(mockTopics).skipAllMessages("persistent://myprop/clust/ns1/ds1", "sub1");
+
+        cmdTopics.run(split("skip persistent://myprop/clust/ns1/ds1 -s sub1 -n 100"));
+        verify(mockTopics).skipMessages("persistent://myprop/clust/ns1/ds1", "sub1", 100);
+
+        cmdTopics.run(split("expire-messages persistent://myprop/clust/ns1/ds1 -s sub1 -t 100"));
+        verify(mockTopics).expireMessages("persistent://myprop/clust/ns1/ds1", "sub1", 100);
+
+        cmdTopics.run(split("expire-messages-all-subscriptions persistent://myprop/clust/ns1/ds1 -t 100"));
+        verify(mockTopics).expireMessagesForAllSubscriptions("persistent://myprop/clust/ns1/ds1", 100);
+
+        cmdTopics.run(split("create-subscription persistent://myprop/clust/ns1/ds1 -s sub1 --messageId earliest"));
+        verify(mockTopics).createSubscription("persistent://myprop/clust/ns1/ds1", "sub1", MessageId.earliest);
+
+        cmdTopics.run(split("create-partitioned-topic persistent://myprop/clust/ns1/ds1 --partitions 32"));
+        verify(mockTopics).createPartitionedTopic("persistent://myprop/clust/ns1/ds1", 32);
+
+        cmdTopics.run(split("list-partitioned-topics myprop/clust/ns1"));
+        verify(mockTopics).getPartitionedTopicList("myprop/clust/ns1");
+
+        cmdTopics.run(split("get-partitioned-topic-metadata persistent://myprop/clust/ns1/ds1"));
+        verify(mockTopics).getPartitionedTopicMetadata("persistent://myprop/clust/ns1/ds1");
+
+        cmdTopics.run(split("delete-partitioned-topic persistent://myprop/clust/ns1/ds1"));
+        verify(mockTopics).deletePartitionedTopic("persistent://myprop/clust/ns1/ds1");
+
+        cmdTopics.run(split("peek-messages persistent://myprop/clust/ns1/ds1 -s sub1 -n 3"));
+        verify(mockTopics).peekMessages("persistent://myprop/clust/ns1/ds1", "sub1", 3);
+
+        // argument matcher for the timestamp in reset cursor. Since we can't verify exact timestamp, we check for a
+        // range of +/- 1 second of the expected timestamp
+        class TimestampMatcher extends ArgumentMatcher<Long> {
+            @Override
+            public boolean matches(Object argument) {
+                long timestamp = (Long) argument;
+                long expectedTimestamp = System.currentTimeMillis() - (1 * 60 * 1000);
+                if (timestamp < (expectedTimestamp + 1000) && timestamp > (expectedTimestamp - 1000)) {
+                    return true;
+                }
+                return false;
+            }
+        }
+        cmdTopics.run(split("reset-cursor persistent://myprop/clust/ns1/ds1 -s sub1 -t 1m"));
+        verify(mockTopics).resetCursor(Matchers.eq("persistent://myprop/clust/ns1/ds1"), Matchers.eq("sub1"),
+                Matchers.longThat(new TimestampMatcher()));
+    }
+
     @Test
     void persistentTopics() throws Exception {
         PulsarAdmin admin = Mockito.mock(PulsarAdmin.class);
diff --git a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolTest.java b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolTest.java
index e05c91413f..2f96358334 100644
--- a/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolTest.java
+++ b/pulsar-client-tools-test/src/test/java/org/apache/pulsar/client/cli/PulsarClientToolTest.java
@@ -80,7 +80,7 @@ public void testInitialzation() throws MalformedURLException, InterruptedExcepti
         // Make sure subscription has been created
         while (true) {
             try {
-                List<String> subscriptions = admin.persistentTopics().getSubscriptions(topicName);
+                List<String> subscriptions = admin.topics().getSubscriptions(topicName);
                 if(subscriptions.size() == 1){
                     break;
                 }
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBase.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBase.java
index bf8ce6c058..5d693d3132 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBase.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdBase.java
@@ -18,15 +18,14 @@
  */
 package org.apache.pulsar.admin.cli;
 
-import javax.ws.rs.WebApplicationException;
-import org.apache.pulsar.client.admin.PulsarAdmin;
-import org.apache.pulsar.client.admin.PulsarAdminException;
-import org.apache.pulsar.client.admin.PulsarAdminException.ConnectException;
-
 import com.beust.jcommander.JCommander;
 import com.beust.jcommander.Parameter;
 import com.beust.jcommander.ParameterException;
 
+import org.apache.pulsar.client.admin.PulsarAdmin;
+import org.apache.pulsar.client.admin.PulsarAdminException;
+import org.apache.pulsar.client.admin.PulsarAdminException.ConnectException;
+
 public abstract class CmdBase {
     protected final JCommander jcommander;
     protected final PulsarAdmin admin;
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNonPersistentTopics.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNonPersistentTopics.java
index 0bab34c675..37f60248bc 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNonPersistentTopics.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdNonPersistentTopics.java
@@ -18,14 +18,15 @@
  */
 package org.apache.pulsar.admin.cli;
 
-import org.apache.pulsar.client.admin.NonPersistentTopics;
-
 import com.beust.jcommander.Parameter;
 import com.beust.jcommander.Parameters;
+
+import org.apache.pulsar.client.admin.NonPersistentTopics;
 import org.apache.pulsar.client.admin.PulsarAdmin;
 import org.apache.pulsar.client.admin.PulsarAdminException;
 
-@Parameters(commandDescription = "Operations on non-persistent topics")
+@SuppressWarnings("deprecation")
+@Parameters(commandDescription = "Operations on non-persistent topics", hidden = true)
 public class CmdNonPersistentTopics extends CmdBase {
     private final NonPersistentTopics nonPersistentTopics;
 
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPersistentTopics.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPersistentTopics.java
index dfc72e2275..5b652561a6 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPersistentTopics.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdPersistentTopics.java
@@ -44,7 +44,8 @@
 import io.netty.buffer.ByteBufUtil;
 import io.netty.buffer.Unpooled;
 
-@Parameters(commandDescription = "Operations on persistent topics")
+@SuppressWarnings("deprecation")
+@Parameters(commandDescription = "Operations on persistent topics", hidden = true)
 public class CmdPersistentTopics extends CmdBase {
     private final PersistentTopics persistentTopics;
 
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopics.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopics.java
new file mode 100644
index 0000000000..04d96902fa
--- /dev/null
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdTopics.java
@@ -0,0 +1,643 @@
+/**
+ * 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.pulsar.admin.cli;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
+
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
+import com.beust.jcommander.converters.CommaParameterSplitter;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.pulsar.client.admin.PulsarAdmin;
+import org.apache.pulsar.client.admin.PulsarAdminException;
+import org.apache.pulsar.client.admin.Topics;
+import org.apache.pulsar.client.api.Message;
+import org.apache.pulsar.client.api.MessageId;
+import org.apache.pulsar.client.impl.BatchMessageIdImpl;
+import org.apache.pulsar.client.impl.MessageIdImpl;
+import org.apache.pulsar.common.compaction.CompactionStatus;
+
+@Parameters(commandDescription = "Operations on persistent topics")
+public class CmdTopics extends CmdBase {
+    private final Topics topics;
+
+    public CmdTopics(PulsarAdmin admin) {
+        super("topics", admin);
+        topics = admin.topics();
+
+        jcommander.addCommand("list", new ListCmd());
+        jcommander.addCommand("list-partitioned-topics", new PartitionedTopicListCmd());
+        jcommander.addCommand("permissions", new Permissions());
+        jcommander.addCommand("grant-permission", new GrantPermissions());
+        jcommander.addCommand("revoke-permission", new RevokePermissions());
+        jcommander.addCommand("lookup", new Lookup());
+        jcommander.addCommand("bundle-range", new GetBundleRange());
+        jcommander.addCommand("delete", new DeleteCmd());
+        jcommander.addCommand("unload", new UnloadCmd());
+        jcommander.addCommand("subscriptions", new ListSubscriptions());
+        jcommander.addCommand("unsubscribe", new DeleteSubscription());
+        jcommander.addCommand("create-subscription", new CreateSubscription());
+
+        jcommander.addCommand("stats", new GetStats());
+        jcommander.addCommand("stats-internal", new GetInternalStats());
+        jcommander.addCommand("info-internal", new GetInternalInfo());
+
+        jcommander.addCommand("partitioned-stats", new GetPartitionedStats());
+
+        jcommander.addCommand("skip", new Skip());
+        jcommander.addCommand("clear-backlog", new ClearBacklog());
+
+        jcommander.addCommand("expire-messages", new ExpireMessages());
+        jcommander.addCommand("expire-messages-all-subscriptions", new ExpireMessagesForAllSubscriptions());
+
+        jcommander.addCommand("create-partitioned-topic", new CreatePartitionedCmd());
+        jcommander.addCommand("update-partitioned-topic", new UpdatePartitionedCmd());
+        jcommander.addCommand("get-partitioned-topic-metadata", new GetPartitionedTopicMetadataCmd());
+
+        jcommander.addCommand("delete-partitioned-topic", new DeletePartitionedCmd());
+        jcommander.addCommand("peek-messages", new PeekMessages());
+        jcommander.addCommand("reset-cursor", new ResetCursor());
+        jcommander.addCommand("terminate", new Terminate());
+        jcommander.addCommand("compact", new Compact());
+        jcommander.addCommand("compaction-status", new CompactionStatusCmd());
+    }
+
+    @Parameters(commandDescription = "Get the list of topics under a namespace.")
+    private class ListCmd extends CliCommand {
+        @Parameter(description = "property/namespace\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String namespace = validateNamespace(params);
+            print(topics.getList(namespace));
+        }
+    }
+
+    @Parameters(commandDescription = "Get the list of partitioned topics under a namespace.")
+    private class PartitionedTopicListCmd extends CliCommand {
+        @Parameter(description = "property/namespace\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String namespace = validateNamespace(params);
+            print(topics.getPartitionedTopicList(namespace));
+        }
+    }
+
+    @Parameters(commandDescription = "Grant a new permission to a client role on a single topic.")
+    private class GrantPermissions extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = "--role", description = "Client role to which grant permissions", required = true)
+        private String role;
+
+        @Parameter(names = "--actions", description = "Actions to be granted (produce,consume)", required = true, splitter = CommaParameterSplitter.class)
+        private List<String> actions;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            topics.grantPermission(topic, role, getAuthActions(actions));
+        }
+    }
+
+    @Parameters(commandDescription = "Revoke permissions on a topic \n "
+            + "\t\t\t   Revoke permissions to a client role on a single topic. If the permission \n"
+            + "\t\t\t   was not set at the topic level, but rather at the namespace level, this \n"
+            + "\t\t\t   operation will return an error (HTTP status code 412).")
+    private class RevokePermissions extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = "--role", description = "Client role to which revoke permissions", required = true)
+        private String role;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            topics.revokePermissions(topic, role);
+        }
+    }
+
+    @Parameters(commandDescription = "Get the permissions on a topic\n"
+            + "\t\t     Retrieve the effective permissions for a topic. These permissions are defined \n"
+            + "\t\t     by the permissions set at the namespace level combined (union) with any eventual \n"
+            + "\t\t     specific permission set on the topic.")
+    private class Permissions extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            print(topics.getPermissions(topic));
+        }
+    }
+
+    @Parameters(commandDescription = "Lookup a topic from the current serving broker")
+    private class Lookup extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            print(admin.lookups().lookupTopic(topic));
+        }
+    }
+
+    @Parameters(commandDescription = "Get Namespace bundle range of a topic")
+    private class GetBundleRange extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            print(admin.lookups().getBundleRange(topic));
+        }
+    }
+
+    @Parameters(commandDescription = "Create a partitioned topic. \n"
+            + "\t\tThe partitioned topic has to be created before creating a producer on it.")
+    private class CreatePartitionedCmd extends CliCommand {
+
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-p",
+                "--partitions" }, description = "Number of partitions for the topic", required = true)
+        private int numPartitions;
+
+        @Override
+        void run() throws Exception {
+            String topic = validateTopicName(params);
+            topics.createPartitionedTopic(topic, numPartitions);
+        }
+    }
+
+    @Parameters(commandDescription = "Update existing non-global partitioned topic. \n"
+            + "\t\tNew updating number of partitions must be greater than existing number of partitions.")
+    private class UpdatePartitionedCmd extends CliCommand {
+
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-p",
+                "--partitions" }, description = "Number of partitions for the topic", required = true)
+        private int numPartitions;
+
+        @Override
+        void run() throws Exception {
+            String topic = validateTopicName(params);
+            topics.updatePartitionedTopic(topic, numPartitions);
+        }
+    }
+
+    @Parameters(commandDescription = "Get the partitioned topic metadata. \n"
+            + "\t\tIf the topic is not created or is a non-partitioned topic, it returns empty topic with 0 partitions")
+    private class GetPartitionedTopicMetadataCmd extends CliCommand {
+
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws Exception {
+            String topic = validateTopicName(params);
+            print(topics.getPartitionedTopicMetadata(topic));
+        }
+    }
+
+    @Parameters(commandDescription = "Delete a partitioned topic. \n"
+            + "\t\tIt will also delete all the partitions of the topic if it exists.")
+    private class DeletePartitionedCmd extends CliCommand {
+
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws Exception {
+            String topic = validateTopicName(params);
+            topics.deletePartitionedTopic(topic);
+        }
+    }
+
+    @Parameters(commandDescription = "Delete a topic. \n"
+            + "\t\tThe topic cannot be deleted if there's any active subscription or producers connected to it.")
+    private class DeleteCmd extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            topics.delete(topic);
+        }
+    }
+
+    @Parameters(commandDescription = "Unload a topic. \n")
+    private class UnloadCmd extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            topics.unload(topic);
+        }
+    }
+
+    @Parameters(commandDescription = "Get the list of subscriptions on the topic")
+    private class ListSubscriptions extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws Exception {
+            String topic = validateTopicName(params);
+            print(topics.getSubscriptions(topic));
+        }
+    }
+
+    @Parameters(commandDescription = "Delete a durable subscriber from a topic. \n"
+            + "\t\tThe subscription cannot be deleted if there are any active consumers attached to it \n")
+    private class DeleteSubscription extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-s", "--subscription" }, description = "Subscription to be deleted", required = true)
+        private String subName;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            topics.deleteSubscription(topic, subName);
+        }
+    }
+
+    @Parameters(commandDescription = "Get the stats for the topic and its connected producers and consumers. \n"
+            + "\t       All the rates are computed over a 1 minute window and are relative the last completed 1 minute period.")
+    private class GetStats extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            print(topics.getStats(topic));
+        }
+    }
+
+    @Parameters(commandDescription = "Get the internal stats for the topic")
+    private class GetInternalStats extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            print(topics.getInternalStats(topic));
+        }
+    }
+
+    @Parameters(commandDescription = "Get the internal metadata info for the topic")
+    private class GetInternalInfo extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            JsonObject result = topics.getInternalInfo(topic);
+            Gson gson = new GsonBuilder().setPrettyPrinting().create();
+            System.out.println(gson.toJson(result));
+        }
+    }
+
+    @Parameters(commandDescription = "Get the stats for the partitioned topic and its connected producers and consumers. \n"
+            + "\t       All the rates are computed over a 1 minute window and are relative the last completed 1 minute period.")
+    private class GetPartitionedStats extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic\n", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = "--per-partition", description = "Get per partition stats")
+        private boolean perPartition = false;
+
+        @Override
+        void run() throws Exception {
+            String topic = validateTopicName(params);
+            print(topics.getPartitionedStats(topic, perPartition));
+        }
+    }
+
+    @Parameters(commandDescription = "Skip all the messages for the subscription")
+    private class ClearBacklog extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-s", "--subscription" }, description = "Subscription to be cleared", required = true)
+        private String subName;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            topics.skipAllMessages(topic, subName);
+        }
+    }
+
+    @Parameters(commandDescription = "Skip some messages for the subscription")
+    private class Skip extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-s",
+                "--subscription" }, description = "Subscription to be skip messages on", required = true)
+        private String subName;
+
+        @Parameter(names = { "-n", "--count" }, description = "Number of messages to skip", required = true)
+        private long numMessages;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            topics.skipMessages(topic, subName, numMessages);
+        }
+    }
+
+    @Parameters(commandDescription = "Expire messages that older than given expiry time (in seconds) for the subscription")
+    private class ExpireMessages extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-s",
+                "--subscription" }, description = "Subscription to be skip messages on", required = true)
+        private String subName;
+
+        @Parameter(names = { "-t", "--expireTime" }, description = "Expire messages older than time in seconds", required = true)
+        private long expireTimeInSeconds;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            topics.expireMessages(topic, subName, expireTimeInSeconds);
+        }
+    }
+
+    @Parameters(commandDescription = "Expire messages that older than given expiry time (in seconds) for all subscriptions")
+    private class ExpireMessagesForAllSubscriptions extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-t", "--expireTime" }, description = "Expire messages older than time in seconds", required = true)
+        private long expireTimeInSeconds;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            topics.expireMessagesForAllSubscriptions(topic, expireTimeInSeconds);
+        }
+    }
+
+    @Parameters(commandDescription = "Create a new subscription on a topic")
+    private class CreateSubscription extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-s",
+                "--subscription" }, description = "Subscription to reset position on", required = true)
+        private String subscriptionName;
+
+        @Parameter(names = { "--messageId",
+                "-m" }, description = "messageId where to create the subscription. It can be either 'latest', 'earliest' or (ledgerId:entryId)", required = false)
+        private String messageIdStr = "latest";
+
+        @Override
+        void run() throws PulsarAdminException {
+            String topic = validateTopicName(params);
+            MessageId messageId;
+            if (messageIdStr.equals("latest")) {
+                messageId = MessageId.latest;
+            } else if (messageIdStr.equals("earliest")) {
+                messageId = MessageId.earliest;
+            } else {
+                messageId = validateMessageIdString(messageIdStr);
+            }
+
+            topics.createSubscription(topic, subscriptionName, messageId);
+        }
+    }
+
+    @Parameters(commandDescription = "Reset position for subscription to position closest to timestamp or messageId")
+    private class ResetCursor extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-s",
+                "--subscription" }, description = "Subscription to reset position on", required = true)
+        private String subName;
+
+        @Parameter(names = { "--time",
+                "-t" }, description = "time in minutes to reset back to (or minutes, hours,days,weeks eg: 100m, 3h, 2d, 5w)", required = false)
+        private String resetTimeStr;
+
+        @Parameter(names = { "--messageId",
+                "-m" }, description = "messageId to reset back to (ledgerId:entryId)", required = false)
+        private String resetMessageIdStr;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String persistentTopic = validatePersistentTopic(params);
+            if (isNotBlank(resetMessageIdStr)) {
+                MessageId messageId = validateMessageIdString(resetMessageIdStr);
+                topics.resetCursor(persistentTopic, subName, messageId);
+            } else if (isNotBlank(resetTimeStr)) {
+                int resetBackTimeInMin = validateTimeString(resetTimeStr);
+                long resetTimeInMillis = TimeUnit.MILLISECONDS.convert(resetBackTimeInMin, TimeUnit.MINUTES);
+                // now - go back time
+                long timestamp = System.currentTimeMillis() - resetTimeInMillis;
+                topics.resetCursor(persistentTopic, subName, timestamp);
+            } else {
+                throw new PulsarAdminException(
+                        "Either Timestamp (--time) or Position (--position) has to be provided to reset cursor");
+            }
+        }
+    }
+
+    @Parameters(commandDescription = "Terminate a topic and don't allow any more messages to be published")
+    private class Terminate extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String persistentTopic = validatePersistentTopic(params);
+
+            try {
+                MessageId lastMessageId = topics.terminateTopicAsync(persistentTopic).get();
+                System.out.println("Topic succesfully terminated at " + lastMessageId);
+            } catch (InterruptedException | ExecutionException e) {
+                throw new PulsarAdminException(e);
+            }
+        }
+    }
+
+    @Parameters(commandDescription = "Peek some messages for the subscription")
+    private class PeekMessages extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-s",
+                "--subscription" }, description = "Subscription to get messages from", required = true)
+        private String subName;
+
+        @Parameter(names = { "-n", "--count" }, description = "Number of messages (default 1)", required = false)
+        private int numMessages = 1;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String persistentTopic = validatePersistentTopic(params);
+            List<Message<byte[]>> messages = topics.peekMessages(persistentTopic, subName, numMessages);
+            int position = 0;
+            for (Message<byte[]> msg : messages) {
+                if (++position != 1) {
+                    System.out.println("-------------------------------------------------------------------------\n");
+                }
+                if (msg.getMessageId() instanceof BatchMessageIdImpl) {
+                    BatchMessageIdImpl msgId = (BatchMessageIdImpl) msg.getMessageId();
+                    System.out.println("Batch Message ID: " + msgId.getLedgerId() + ":" + msgId.getEntryId() + ":" + msgId.getBatchIndex());
+                } else {
+                    MessageIdImpl msgId = (MessageIdImpl) msg.getMessageId();
+                    System.out.println("Message ID: " + msgId.getLedgerId() + ":" + msgId.getEntryId());
+                }
+                if (msg.getProperties().size() > 0) {
+                    System.out.println("Tenants:");
+                    print(msg.getProperties());
+                }
+                ByteBuf data = Unpooled.wrappedBuffer(msg.getData());
+                System.out.println(ByteBufUtil.prettyHexDump(data));
+            }
+        }
+    }
+
+    @Parameters(commandDescription = "Compact a topic")
+    private class Compact extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String persistentTopic = validatePersistentTopic(params);
+
+            topics.triggerCompaction(persistentTopic);
+            System.out.println("Topic compaction requested for " + persistentTopic);
+        }
+    }
+
+    @Parameters(commandDescription = "Status of compaction on a topic")
+    private class CompactionStatusCmd extends CliCommand {
+        @Parameter(description = "persistent://property/namespace/topic", required = true)
+        private java.util.List<String> params;
+
+        @Parameter(names = { "-w", "--wait-complete" },
+                   description = "Wait for compaction to complete", required = false)
+        private boolean wait = false;
+
+        @Override
+        void run() throws PulsarAdminException {
+            String persistentTopic = validatePersistentTopic(params);
+
+            try {
+                CompactionStatus status = topics.compactionStatus(persistentTopic);
+                while (wait && status.status == CompactionStatus.Status.RUNNING) {
+                    Thread.sleep(1000);
+                    status = topics.compactionStatus(persistentTopic);
+                }
+
+                switch (status.status) {
+                case NOT_RUN:
+                    System.out.println("Compaction has not been run for " + persistentTopic
+                                       + " since broker startup");
+                    break;
+                case RUNNING:
+                    System.out.println("Compaction is currently running");
+                    break;
+                case SUCCESS:
+                    System.out.println("Compaction was a success");
+                    break;
+                case ERROR:
+                    System.out.println("Error in compaction");
+                    throw new PulsarAdminException("Error compacting: " + status.lastError);
+                }
+            } catch (InterruptedException e) {
+                throw new PulsarAdminException(e);
+            }
+        }
+    }
+
+    private static int validateTimeString(String s) {
+        char last = s.charAt(s.length() - 1);
+        String subStr = s.substring(0, s.length() - 1);
+        switch (last) {
+        case 'm':
+        case 'M':
+            return Integer.parseInt(subStr);
+
+        case 'h':
+        case 'H':
+            return Integer.parseInt(subStr) * 60;
+
+        case 'd':
+        case 'D':
+            return Integer.parseInt(subStr) * 24 * 60;
+
+        case 'w':
+        case 'W':
+            return Integer.parseInt(subStr) * 7 * 24 * 60;
+
+        default:
+            return Integer.parseInt(s);
+        }
+    }
+
+    private MessageId validateMessageIdString(String resetMessageIdStr) throws PulsarAdminException {
+        String[] messageId = resetMessageIdStr.split(":");
+        try {
+            checkArgument(messageId.length == 2);
+            return new MessageIdImpl(Long.parseLong(messageId[0]), Long.parseLong(messageId[1]), -1);
+        } catch (Exception e) {
+            throw new PulsarAdminException(
+                    "Invalid reset-position (must be in format: ledgerId:entryId) value " + resetMessageIdStr);
+        }
+    }
+}
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java
index c7b0335e6b..29d016f5c5 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java
@@ -74,8 +74,13 @@
         commandMap.put("tenants", CmdTenants.class);
         commandMap.put("properties", CmdTenants.CmdProperties.class); // deprecated, doesn't show in usage()
         commandMap.put("namespaces", CmdNamespaces.class);
+        commandMap.put("topics", CmdTopics.class);
+
+        // Hidden deprecated "persistent" and "non-persistent" subcommands
         commandMap.put("persistent", CmdPersistentTopics.class);
         commandMap.put("non-persistent", CmdNonPersistentTopics.class);
+
+
         commandMap.put("resource-quotas", CmdResourceQuotas.class);
         commandMap.put("functions", CmdFunctions.class);
     }
diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java b/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java
index 5e9d43e735..ba5da90aa5 100644
--- a/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java
+++ b/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java
@@ -269,14 +269,18 @@ public static int getPartitionIndex(String topic) {
 
     /**
      * Returns the http rest path for use in the admin web service
+     * Eg:
+     *
+     *   * "persistent/my-tenant/my-namespace/my-topic"
+     *   * "non-persistent/my-tenant/my-namespace/my-topic"
      *
      * @return topic rest path
      */
     public String getRestPath() {
         if (isV2()) {
-            return String.format("%s/%s/%s", tenant, namespacePortion, getEncodedLocalName());
+            return String.format("%s/%s/%s/%s", domain, tenant, namespacePortion, getEncodedLocalName());
         } else {
-            return String.format("%s/%s/%s/%s", tenant, cluster, namespacePortion, getEncodedLocalName());
+            return String.format("%s/%s/%s/%s/%s", domain, tenant, cluster, namespacePortion, getEncodedLocalName());
         }
     }
 
diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NonPersistentTopicStats.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NonPersistentTopicStats.java
index c154426efa..bef5609339 100644
--- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NonPersistentTopicStats.java
+++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NonPersistentTopicStats.java
@@ -28,7 +28,7 @@
 
 /**
  */
-public class NonPersistentTopicStats extends PersistentTopicStats {
+public class NonPersistentTopicStats extends TopicStats {
 
     /**
      * for non-persistent topic: broker drops msg if publisher publishes messages more than configured max inflight
diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PartitionedTopicStats.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PartitionedTopicStats.java
index 6922febe6e..4db5397897 100644
--- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PartitionedTopicStats.java
+++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PartitionedTopicStats.java
@@ -24,11 +24,11 @@
 
 import com.google.common.collect.Maps;
 
-public class PartitionedTopicStats extends PersistentTopicStats {
+public class PartitionedTopicStats extends TopicStats {
 
     public PartitionedTopicMetadata metadata;
 
-    public Map<String, PersistentTopicStats> partitions;
+    public Map<String, TopicStats> partitions;
 
     public PartitionedTopicStats() {
         super();
diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PersistentTopicStats.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PersistentTopicStats.java
index cc5463d98c..c093e4493b 100644
--- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PersistentTopicStats.java
+++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PersistentTopicStats.java
@@ -18,105 +18,9 @@
  */
 package org.apache.pulsar.common.policies.data;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.List;
-import java.util.Map;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-
 /**
+ * Maintain compatibility with previous class name
  */
-public class PersistentTopicStats {
-    /** Total rate of messages published on the topic. msg/s */
-    public double msgRateIn;
-
-    /** Total throughput of messages published on the topic. byte/s */
-    public double msgThroughputIn;
-
-    /** Total rate of messages dispatched for the topic. msg/s */
-    public double msgRateOut;
-
-    /** Total throughput of messages dispatched for the topic. byte/s */
-    public double msgThroughputOut;
-
-    /** Average size of published messages. bytes */
-    public double averageMsgSize;
-
-    /** Space used to store the messages for the topic. bytes */
-    public long storageSize;
-
-    /** List of connected publishers on this topic w/ their stats */
-    public List<PublisherStats> publishers;
-
-    /** Map of subscriptions with their individual statistics */
-    public Map<String, SubscriptionStats> subscriptions;
-
-    /** Map of replication statistics by remote cluster context */
-    public Map<String, ReplicatorStats> replication;
-
-    public String deduplicationStatus;
-
-    public PersistentTopicStats() {
-        this.publishers = Lists.newArrayList();
-        this.subscriptions = Maps.newHashMap();
-        this.replication = Maps.newTreeMap();
-    }
-
-    public void reset() {
-        this.msgRateIn = 0;
-        this.msgThroughputIn = 0;
-        this.msgRateOut = 0;
-        this.msgThroughputOut = 0;
-        this.averageMsgSize = 0;
-        this.storageSize = 0;
-        this.publishers.clear();
-        this.subscriptions.clear();
-        this.replication.clear();
-        this.deduplicationStatus = null;
-    }
+public class PersistentTopicStats extends TopicStats {
 
-    // if the stats are added for the 1st time, we will need to make a copy of these stats and add it to the current
-    // stats
-    public PersistentTopicStats add(PersistentTopicStats stats) {
-        checkNotNull(stats);
-        this.msgRateIn += stats.msgRateIn;
-        this.msgThroughputIn += stats.msgThroughputIn;
-        this.msgRateOut += stats.msgRateOut;
-        this.msgThroughputOut += stats.msgThroughputOut;
-        this.averageMsgSize += stats.averageMsgSize;
-        this.storageSize += stats.storageSize;
-        if (this.publishers.size() != stats.publishers.size()) {
-            for (int i = 0; i < stats.publishers.size(); i++) {
-                PublisherStats publisherStats = new PublisherStats();
-                this.publishers.add(publisherStats.add(stats.publishers.get(i)));
-            }
-        } else {
-            for (int i = 0; i < stats.publishers.size(); i++) {
-                this.publishers.get(i).add(stats.publishers.get(i));
-            }
-        }
-        if (this.subscriptions.size() != stats.subscriptions.size()) {
-            for (String subscription : stats.subscriptions.keySet()) {
-                SubscriptionStats subscriptionStats = new SubscriptionStats();
-                this.subscriptions.put(subscription, subscriptionStats.add(stats.subscriptions.get(subscription)));
-            }
-        } else {
-            for (String subscription : stats.subscriptions.keySet()) {
-                this.subscriptions.get(subscription).add(stats.subscriptions.get(subscription));
-            }
-        }
-        if (this.replication.size() != stats.replication.size()) {
-            for (String repl : stats.replication.keySet()) {
-                ReplicatorStats replStats = new ReplicatorStats();
-                this.replication.put(repl, replStats.add(stats.replication.get(repl)));
-            }
-        } else {
-            for (String repl : stats.replication.keySet()) {
-                this.replication.get(repl).add(stats.replication.get(repl));
-            }
-        }
-        return this;
-    }
 }
diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicStats.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicStats.java
new file mode 100644
index 0000000000..4487e6e2f3
--- /dev/null
+++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicStats.java
@@ -0,0 +1,122 @@
+/**
+ * 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.pulsar.common.policies.data;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ */
+public class TopicStats {
+    /** Total rate of messages published on the topic. msg/s */
+    public double msgRateIn;
+
+    /** Total throughput of messages published on the topic. byte/s */
+    public double msgThroughputIn;
+
+    /** Total rate of messages dispatched for the topic. msg/s */
+    public double msgRateOut;
+
+    /** Total throughput of messages dispatched for the topic. byte/s */
+    public double msgThroughputOut;
+
+    /** Average size of published messages. bytes */
+    public double averageMsgSize;
+
+    /** Space used to store the messages for the topic. bytes */
+    public long storageSize;
+
+    /** List of connected publishers on this topic w/ their stats */
+    public List<PublisherStats> publishers;
+
+    /** Map of subscriptions with their individual statistics */
+    public Map<String, SubscriptionStats> subscriptions;
+
+    /** Map of replication statistics by remote cluster context */
+    public Map<String, ReplicatorStats> replication;
+
+    public String deduplicationStatus;
+
+    public TopicStats() {
+        this.publishers = Lists.newArrayList();
+        this.subscriptions = Maps.newHashMap();
+        this.replication = Maps.newTreeMap();
+    }
+
+    public void reset() {
+        this.msgRateIn = 0;
+        this.msgThroughputIn = 0;
+        this.msgRateOut = 0;
+        this.msgThroughputOut = 0;
+        this.averageMsgSize = 0;
+        this.storageSize = 0;
+        this.publishers.clear();
+        this.subscriptions.clear();
+        this.replication.clear();
+        this.deduplicationStatus = null;
+    }
+
+    // if the stats are added for the 1st time, we will need to make a copy of these stats and add it to the current
+    // stats
+    public TopicStats add(TopicStats stats) {
+        checkNotNull(stats);
+        this.msgRateIn += stats.msgRateIn;
+        this.msgThroughputIn += stats.msgThroughputIn;
+        this.msgRateOut += stats.msgRateOut;
+        this.msgThroughputOut += stats.msgThroughputOut;
+        this.averageMsgSize += stats.averageMsgSize;
+        this.storageSize += stats.storageSize;
+        if (this.publishers.size() != stats.publishers.size()) {
+            for (int i = 0; i < stats.publishers.size(); i++) {
+                PublisherStats publisherStats = new PublisherStats();
+                this.publishers.add(publisherStats.add(stats.publishers.get(i)));
+            }
+        } else {
+            for (int i = 0; i < stats.publishers.size(); i++) {
+                this.publishers.get(i).add(stats.publishers.get(i));
+            }
+        }
+        if (this.subscriptions.size() != stats.subscriptions.size()) {
+            for (String subscription : stats.subscriptions.keySet()) {
+                SubscriptionStats subscriptionStats = new SubscriptionStats();
+                this.subscriptions.put(subscription, subscriptionStats.add(stats.subscriptions.get(subscription)));
+            }
+        } else {
+            for (String subscription : stats.subscriptions.keySet()) {
+                this.subscriptions.get(subscription).add(stats.subscriptions.get(subscription));
+            }
+        }
+        if (this.replication.size() != stats.replication.size()) {
+            for (String repl : stats.replication.keySet()) {
+                ReplicatorStats replStats = new ReplicatorStats();
+                this.replication.put(repl, replStats.add(stats.replication.get(repl)));
+            }
+        } else {
+            for (String repl : stats.replication.keySet()) {
+                this.replication.get(repl).add(stats.replication.get(repl));
+            }
+        }
+        return this;
+    }
+}
diff --git a/pulsar-common/src/test/java/org/apache/pulsar/common/policies/data/PersistentTopicStatsTest.java b/pulsar-common/src/test/java/org/apache/pulsar/common/policies/data/PersistentTopicStatsTest.java
index 5f21397726..ec3be1cd75 100644
--- a/pulsar-common/src/test/java/org/apache/pulsar/common/policies/data/PersistentTopicStatsTest.java
+++ b/pulsar-common/src/test/java/org/apache/pulsar/common/policies/data/PersistentTopicStatsTest.java
@@ -21,7 +21,7 @@
 import static org.testng.Assert.assertEquals;
 
 import org.apache.pulsar.common.policies.data.SubscriptionStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.common.policies.data.PublisherStats;
 import org.apache.pulsar.common.policies.data.ReplicatorStats;
 import org.testng.annotations.Test;
@@ -30,37 +30,37 @@
 
     @Test
     public void testPersistentTopicStats() {
-        PersistentTopicStats persistentTopicStats = new PersistentTopicStats();
-        persistentTopicStats.msgRateIn = 1;
-        persistentTopicStats.msgThroughputIn = 1;
-        persistentTopicStats.msgRateOut = 1;
-        persistentTopicStats.msgThroughputOut = 1;
-        persistentTopicStats.averageMsgSize = 1;
-        persistentTopicStats.storageSize = 1;
-        persistentTopicStats.publishers.add(new PublisherStats());
-        persistentTopicStats.subscriptions.put("test_ns", new SubscriptionStats());
-        persistentTopicStats.replication.put("test_ns", new ReplicatorStats());
-        PersistentTopicStats target = new PersistentTopicStats();
-        target.add(persistentTopicStats);
-        assertEquals(persistentTopicStats.msgRateIn, 1.0);
-        assertEquals(persistentTopicStats.msgThroughputIn, 1.0);
-        assertEquals(persistentTopicStats.msgRateOut, 1.0);
-        assertEquals(persistentTopicStats.msgThroughputOut, 1.0);
-        assertEquals(persistentTopicStats.averageMsgSize, 1.0);
-        assertEquals(persistentTopicStats.storageSize, 1);
-        assertEquals(persistentTopicStats.publishers.size(), 1);
-        assertEquals(persistentTopicStats.subscriptions.size(), 1);
-        assertEquals(persistentTopicStats.replication.size(), 1);
-        persistentTopicStats.reset();
-        assertEquals(persistentTopicStats.msgRateIn, 0.0);
-        assertEquals(persistentTopicStats.msgThroughputIn, 0.0);
-        assertEquals(persistentTopicStats.msgRateOut, 0.0);
-        assertEquals(persistentTopicStats.msgThroughputOut, 0.0);
-        assertEquals(persistentTopicStats.averageMsgSize, 0.0);
-        assertEquals(persistentTopicStats.storageSize, 0);
-        assertEquals(persistentTopicStats.publishers.size(), 0);
-        assertEquals(persistentTopicStats.subscriptions.size(), 0);
-        assertEquals(persistentTopicStats.replication.size(), 0);
+        TopicStats topicStats = new TopicStats();
+        topicStats.msgRateIn = 1;
+        topicStats.msgThroughputIn = 1;
+        topicStats.msgRateOut = 1;
+        topicStats.msgThroughputOut = 1;
+        topicStats.averageMsgSize = 1;
+        topicStats.storageSize = 1;
+        topicStats.publishers.add(new PublisherStats());
+        topicStats.subscriptions.put("test_ns", new SubscriptionStats());
+        topicStats.replication.put("test_ns", new ReplicatorStats());
+        TopicStats target = new TopicStats();
+        target.add(topicStats);
+        assertEquals(topicStats.msgRateIn, 1.0);
+        assertEquals(topicStats.msgThroughputIn, 1.0);
+        assertEquals(topicStats.msgRateOut, 1.0);
+        assertEquals(topicStats.msgThroughputOut, 1.0);
+        assertEquals(topicStats.averageMsgSize, 1.0);
+        assertEquals(topicStats.storageSize, 1);
+        assertEquals(topicStats.publishers.size(), 1);
+        assertEquals(topicStats.subscriptions.size(), 1);
+        assertEquals(topicStats.replication.size(), 1);
+        topicStats.reset();
+        assertEquals(topicStats.msgRateIn, 0.0);
+        assertEquals(topicStats.msgThroughputIn, 0.0);
+        assertEquals(topicStats.msgRateOut, 0.0);
+        assertEquals(topicStats.msgThroughputOut, 0.0);
+        assertEquals(topicStats.averageMsgSize, 0.0);
+        assertEquals(topicStats.storageSize, 0);
+        assertEquals(topicStats.publishers.size(), 0);
+        assertEquals(topicStats.subscriptions.size(), 0);
+        assertEquals(topicStats.replication.size(), 0);
     }
 
 }
\ No newline at end of file
diff --git a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/MembershipManager.java b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/MembershipManager.java
index f43311d926..dde017fe96 100644
--- a/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/MembershipManager.java
+++ b/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/MembershipManager.java
@@ -46,7 +46,7 @@
 import org.apache.pulsar.client.api.PulsarClientException;
 import org.apache.pulsar.client.api.SubscriptionType;
 import org.apache.pulsar.common.policies.data.ConsumerStats;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.apache.pulsar.functions.proto.Function;
 import org.apache.pulsar.functions.utils.FunctionDetailsUtils;
 
@@ -118,18 +118,17 @@ public boolean isLeader() {
     public List<WorkerInfo> getCurrentMembership() {
 
         List<WorkerInfo> workerIds = new LinkedList<>();
-        PersistentTopicStats persistentTopicStats = null;
+        TopicStats topicStats = null;
         PulsarAdmin pulsarAdmin = this.getPulsarAdminClient();
         try {
-            persistentTopicStats = pulsarAdmin.persistentTopics().getStats(
-                    this.workerConfig.getClusterCoordinationTopic());
+            topicStats = pulsarAdmin.topics().getStats(this.workerConfig.getClusterCoordinationTopic());
         } catch (PulsarAdminException e) {
             log.error("Failed to get status of coordinate topic {}",
                     this.workerConfig.getClusterCoordinationTopic(), e);
             throw new RuntimeException(e);
         }
 
-        for (ConsumerStats consumerStats : persistentTopicStats.subscriptions
+        for (ConsumerStats consumerStats : topicStats.subscriptions
                 .get(COORDINATION_TOPIC_SUBSCRIPTION).consumers) {
             WorkerInfo workerInfo = WorkerInfo.parseFrom(consumerStats.metadata.get(WORKER_IDENTIFIER));
             workerIds.add(workerInfo);
diff --git a/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyTest.java b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyTest.java
index 651056b04b..5c994b03b5 100644
--- a/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyTest.java
+++ b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyTest.java
@@ -117,7 +117,7 @@ public void testPartitions() throws Exception {
         admin.tenants().createTenant("sample", new TenantInfo());
         PulsarClient client = PulsarClient.builder().serviceUrl("pulsar://localhost:" + proxyConfig.getServicePort())
                 .build();
-        admin.persistentTopics().createPartitionedTopic("persistent://sample/test/local/partitioned-topic", 2);
+        admin.topics().createPartitionedTopic("persistent://sample/test/local/partitioned-topic", 2);
 
         Producer<byte[]> producer = client.newProducer()
             .topic("persistent://sample/test/local/partitioned-topic")
diff --git a/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyTlsTest.java b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyTlsTest.java
index fa132ad61d..5243b9e7d2 100644
--- a/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyTlsTest.java
+++ b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyTlsTest.java
@@ -96,7 +96,7 @@ public void testPartitions() throws Exception {
                 .serviceUrl("pulsar+ssl://localhost:" + proxyConfig.getServicePortTls()).enableTls(true)
                 .allowTlsInsecureConnection(false).tlsTrustCertsFilePath(TLS_TRUST_CERT_FILE_PATH).build();
         admin.tenants().createTenant("sample", new TenantInfo());
-        admin.persistentTopics().createPartitionedTopic("persistent://sample/test/local/partitioned-topic", 2);
+        admin.topics().createPartitionedTopic("persistent://sample/test/local/partitioned-topic", 2);
 
         Producer<byte[]> producer = client.newProducer().topic("persistent://sample/test/local/partitioned-topic")
                 .messageRoutingMode(MessageRoutingMode.RoundRobinPartition).create();
diff --git a/pulsar-storm/src/test/java/org/apache/pulsar/storm/PulsarBoltTest.java b/pulsar-storm/src/test/java/org/apache/pulsar/storm/PulsarBoltTest.java
index 6645feb79a..0a17870972 100644
--- a/pulsar-storm/src/test/java/org/apache/pulsar/storm/PulsarBoltTest.java
+++ b/pulsar-storm/src/test/java/org/apache/pulsar/storm/PulsarBoltTest.java
@@ -30,7 +30,7 @@
 import org.apache.pulsar.client.api.Message;
 import org.apache.pulsar.client.api.MessageBuilder;
 import org.apache.pulsar.client.api.ProducerConsumerBase;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 import org.testng.Assert;
 import static org.testng.Assert.fail;
 import org.testng.annotations.AfterMethod;
@@ -188,7 +188,7 @@ public void testMetrics() throws Exception {
 
     @Test
     public void testSharedProducer() throws Exception {
-        PersistentTopicStats topicStats = admin.persistentTopics().getStats(topic);
+        TopicStats topicStats = admin.topics().getStats(topic);
         Assert.assertEquals(topicStats.publishers.size(), 1);
         PulsarBolt otherBolt = new PulsarBolt(pulsarBoltConf, new ClientConfiguration());
         MockOutputCollector otherMockCollector = new MockOutputCollector();
@@ -198,12 +198,12 @@ public void testSharedProducer() throws Exception {
         when(context.getThisTaskId()).thenReturn(1);
         otherBolt.prepare(Maps.newHashMap(), context, collector);
 
-        topicStats = admin.persistentTopics().getStats(topic);
+        topicStats = admin.topics().getStats(topic);
         Assert.assertEquals(topicStats.publishers.size(), 1);
 
         otherBolt.close();
 
-        topicStats = admin.persistentTopics().getStats(topic);
+        topicStats = admin.topics().getStats(topic);
         Assert.assertEquals(topicStats.publishers.size(), 1);
     }
 
diff --git a/pulsar-storm/src/test/java/org/apache/pulsar/storm/PulsarSpoutTest.java b/pulsar-storm/src/test/java/org/apache/pulsar/storm/PulsarSpoutTest.java
index a62649d798..dd7de0f682 100644
--- a/pulsar-storm/src/test/java/org/apache/pulsar/storm/PulsarSpoutTest.java
+++ b/pulsar-storm/src/test/java/org/apache/pulsar/storm/PulsarSpoutTest.java
@@ -41,7 +41,7 @@
 import org.apache.pulsar.client.api.Producer;
 import org.apache.pulsar.client.api.ProducerConsumerBase;
 import org.apache.pulsar.client.api.SubscriptionType;
-import org.apache.pulsar.common.policies.data.PersistentTopicStats;
+import org.apache.pulsar.common.policies.data.TopicStats;
 
 import org.apache.storm.spout.SpoutOutputCollector;
 import org.apache.storm.task.TopologyContext;
@@ -276,7 +276,7 @@ public void testMetrics() throws Exception {
 
     @Test
     public void testSharedConsumer() throws Exception {
-        PersistentTopicStats topicStats = admin.persistentTopics().getStats(topic);
+        TopicStats topicStats = admin.topics().getStats(topic);
         Assert.assertEquals(topicStats.subscriptions.get(subscriptionName).consumers.size(), 1);
         PulsarSpout otherSpout = new PulsarSpout(pulsarSpoutConf, new ClientConfiguration(), consumerConf);
         MockSpoutOutputCollector otherMockCollector = new MockSpoutOutputCollector();
@@ -286,18 +286,18 @@ public void testSharedConsumer() throws Exception {
         when(context.getThisTaskId()).thenReturn(1);
         otherSpout.open(Maps.newHashMap(), context, collector);
 
-        topicStats = admin.persistentTopics().getStats(topic);
+        topicStats = admin.topics().getStats(topic);
         Assert.assertEquals(topicStats.subscriptions.get(subscriptionName).consumers.size(), 1);
 
         otherSpout.close();
 
-        topicStats = admin.persistentTopics().getStats(topic);
+        topicStats = admin.topics().getStats(topic);
         Assert.assertEquals(topicStats.subscriptions.get(subscriptionName).consumers.size(), 1);
     }
 
     @Test
     public void testNoSharedConsumer() throws Exception {
-        PersistentTopicStats topicStats = admin.persistentTopics().getStats(topic);
+        TopicStats topicStats = admin.topics().getStats(topic);
         Assert.assertEquals(topicStats.subscriptions.get(subscriptionName).consumers.size(), 1);
         pulsarSpoutConf.setSharedConsumerEnabled(false);
         PulsarSpout otherSpout = new PulsarSpout(pulsarSpoutConf, new ClientConfiguration(), consumerConf);
@@ -308,12 +308,12 @@ public void testNoSharedConsumer() throws Exception {
         when(context.getThisTaskId()).thenReturn(1);
         otherSpout.open(Maps.newHashMap(), context, collector);
 
-        topicStats = admin.persistentTopics().getStats(topic);
+        topicStats = admin.topics().getStats(topic);
         Assert.assertEquals(topicStats.subscriptions.get(subscriptionName).consumers.size(), 2);
 
         otherSpout.close();
 
-        topicStats = admin.persistentTopics().getStats(topic);
+        topicStats = admin.topics().getStats(topic);
         Assert.assertEquals(topicStats.subscriptions.get(subscriptionName).consumers.size(), 1);
     }
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services