18
18
*/
19
19
package org .apache .pulsar .common .naming ;
20
20
21
- import com .google .common .base .Splitter ;
22
21
import com .google .re2j .Pattern ;
23
22
import edu .umd .cs .findbugs .annotations .SuppressFBWarnings ;
24
23
import java .util .ArrayList ;
@@ -46,7 +45,7 @@ public class TopicName implements ServiceUnitId {
46
45
private final String namespacePortion ;
47
46
private final String localName ;
48
47
49
- private final NamespaceName namespaceName ;
48
+ private volatile NamespaceName namespaceName ;
50
49
51
50
private final int partitionIndex ;
52
51
@@ -111,64 +110,77 @@ public static String getPattern(String topic) {
111
110
return "^" + Pattern .quote (get (topic ).getPartitionedTopicName ().toString ()) + "$" ;
112
111
}
113
112
114
- @ SuppressFBWarnings ("DCN_NULLPOINTER_EXCEPTION" )
115
113
private TopicName (String completeTopicName ) {
114
+ this (completeTopicName , true );
115
+ }
116
+
117
+ /**
118
+ * The constructor from a topic name string. You can leverage {@link TopicName#get(String)} to get benefits from
119
+ * the built-in cache mechanism.
120
+ *
121
+ * @param completeTopicName the topic name
122
+ * @param initializeNamespaceName whether to initializing the internal {@link NamespaceName} field
123
+ */
124
+ @ SuppressFBWarnings ("DCN_NULLPOINTER_EXCEPTION" )
125
+ public TopicName (String completeTopicName , boolean initializeNamespaceName ) {
116
126
try {
117
127
// The topic name can be in two different forms, one is fully qualified topic name,
118
128
// the other one is short topic name
119
- if (!completeTopicName .contains ("://" )) {
129
+ int index = completeTopicName .indexOf ("://" );
130
+ if (index < 0 ) {
120
131
// The short topic name can be:
121
132
// - <topic>
122
133
// - <property>/<namespace>/<topic>
123
- String [] parts = StringUtils .split (completeTopicName , '/' );
124
- if (parts .length == 3 ) {
125
- completeTopicName = TopicDomain .persistent .name () + "://" + completeTopicName ;
126
- } else if (parts .length == 1 ) {
127
- completeTopicName = TopicDomain .persistent .name () + "://"
128
- + PUBLIC_TENANT + "/" + DEFAULT_NAMESPACE + "/" + parts [0 ];
134
+ List <String > parts = splitBySlash (completeTopicName , 0 );
135
+ this .domain = TopicDomain .persistent ;
136
+ this .cluster = null ;
137
+ if (parts .size () == 3 ) {
138
+ this .tenant = parts .get (0 );
139
+ this .namespacePortion = parts .get (1 );
140
+ this .localName = parts .get (2 );
141
+ } else if (parts .size () == 1 ) {
142
+ this .tenant = PUBLIC_TENANT ;
143
+ this .namespacePortion = DEFAULT_NAMESPACE ;
144
+ this .localName = parts .get (0 );
129
145
} else {
130
146
throw new IllegalArgumentException (
131
147
"Invalid short topic name '" + completeTopicName + "', it should be in the format of "
132
148
+ "<tenant>/<namespace>/<topic> or <topic>" );
133
149
}
150
+ this .partitionIndex = getPartitionIndex (localName );
151
+ this .completeTopicName = domain .name () + "://" + tenant + "/" + namespacePortion + "/" + localName ;
152
+ } else {
153
+ // The fully qualified topic name can be in two different forms:
154
+ // new: persistent://tenant/namespace/topic
155
+ // legacy: persistent://tenant/cluster/namespace/topic
156
+ //
157
+ // Examples of localName:
158
+ // 1. some, name, xyz
159
+ // 2. xyz-123, feeder-2
160
+ List <String > parts = splitBySlash (completeTopicName .substring (index + "://" .length ()), 4 );
161
+ if (parts .size () == 3 ) {
162
+ // New topic name without cluster name
163
+ this .cluster = null ;
164
+ this .tenant = parts .get (0 );
165
+ this .namespacePortion = parts .get (1 );
166
+ this .localName = parts .get (2 );
167
+ this .partitionIndex = getPartitionIndex (localName );
168
+ } else if (parts .size () == 4 ) {
169
+ // Legacy topic name that includes cluster name
170
+ this .tenant = parts .get (0 );
171
+ this .cluster = parts .get (1 );
172
+ this .namespacePortion = parts .get (2 );
173
+ this .localName = parts .get (3 );
174
+ this .partitionIndex = getPartitionIndex (localName );
175
+ } else {
176
+ throw new IllegalArgumentException ("Invalid topic name " + completeTopicName );
177
+ }
178
+ this .completeTopicName = completeTopicName ;
179
+ this .domain = TopicDomain .getEnum (completeTopicName .substring (0 , index ));
134
180
}
135
181
136
- // The fully qualified topic name can be in two different forms:
137
- // new: persistent://tenant/namespace/topic
138
- // legacy: persistent://tenant/cluster/namespace/topic
139
-
140
- List <String > parts = Splitter .on ("://" ).limit (2 ).splitToList (completeTopicName );
141
- this .domain = TopicDomain .getEnum (parts .get (0 ));
142
-
143
- String rest = parts .get (1 );
144
-
145
- // The rest of the name can be in different forms:
146
- // new: tenant/namespace/<localName>
147
- // legacy: tenant/cluster/namespace/<localName>
148
- // Examples of localName:
149
- // 1. some, name, xyz
150
- // 2. xyz-123, feeder-2
151
-
152
-
153
- parts = Splitter .on ("/" ).limit (4 ).splitToList (rest );
154
- if (parts .size () == 3 ) {
155
- // New topic name without cluster name
156
- this .tenant = parts .get (0 );
157
- this .cluster = null ;
158
- this .namespacePortion = parts .get (1 );
159
- this .localName = parts .get (2 );
160
- this .partitionIndex = getPartitionIndex (completeTopicName );
161
- this .namespaceName = NamespaceName .get (tenant , namespacePortion );
162
- } else if (parts .size () == 4 ) {
163
- // Legacy topic name that includes cluster name
164
- this .tenant = parts .get (0 );
165
- this .cluster = parts .get (1 );
166
- this .namespacePortion = parts .get (2 );
167
- this .localName = parts .get (3 );
168
- this .partitionIndex = getPartitionIndex (completeTopicName );
169
- this .namespaceName = NamespaceName .get (tenant , cluster , namespacePortion );
170
- } else {
171
- throw new IllegalArgumentException ("Invalid topic name: " + completeTopicName );
182
+ if (initializeNamespaceName ) {
183
+ getNamespaceObject ();
172
184
}
173
185
174
186
if (StringUtils .isBlank (localName )) {
@@ -179,14 +191,6 @@ private TopicName(String completeTopicName) {
179
191
} catch (NullPointerException e ) {
180
192
throw new IllegalArgumentException ("Invalid topic name: " + completeTopicName , e );
181
193
}
182
- if (isV2 ()) {
183
- this .completeTopicName = String .format ("%s://%s/%s/%s" ,
184
- domain , tenant , namespacePortion , localName );
185
- } else {
186
- this .completeTopicName = String .format ("%s://%s/%s/%s/%s" ,
187
- domain , tenant , cluster ,
188
- namespacePortion , localName );
189
- }
190
194
}
191
195
192
196
public boolean isPersistent () {
@@ -201,7 +205,7 @@ public boolean isPersistent() {
201
205
* @return the namespace
202
206
*/
203
207
public String getNamespace () {
204
- return namespaceName .toString ();
208
+ return getNamespaceObject () .toString ();
205
209
}
206
210
207
211
/**
@@ -211,6 +215,16 @@ public String getNamespace() {
211
215
*/
212
216
@ Override
213
217
public NamespaceName getNamespaceObject () {
218
+ if (namespaceName != null ) {
219
+ return namespaceName ;
220
+ }
221
+ synchronized (this ) {
222
+ if (cluster == null ) {
223
+ namespaceName = NamespaceName .get (tenant , namespacePortion );
224
+ } else {
225
+ namespaceName = NamespaceName .get (tenant , cluster , namespacePortion );
226
+ }
227
+ }
214
228
return namespaceName ;
215
229
}
216
230
@@ -282,9 +296,10 @@ public String getPartitionedTopicName() {
282
296
*/
283
297
public static int getPartitionIndex (String topic ) {
284
298
int partitionIndex = -1 ;
285
- if (topic .contains (PARTITIONED_TOPIC_SUFFIX )) {
299
+ int index = topic .lastIndexOf (PARTITIONED_TOPIC_SUFFIX );
300
+ if (index >= 0 ) {
286
301
try {
287
- String idx = StringUtils . substringAfterLast ( topic , PARTITIONED_TOPIC_SUFFIX );
302
+ String idx = topic . substring ( index + PARTITIONED_TOPIC_SUFFIX . length () );
288
303
partitionIndex = Integer .parseInt (idx );
289
304
if (partitionIndex < 0 ) {
290
305
// for the "topic-partition--1"
@@ -356,10 +371,10 @@ public String getPersistenceNamingEncoding() {
356
371
public static String fromPersistenceNamingEncoding (String mlName ) {
357
372
// The managedLedgerName convention is: tenant/namespace/domain/topic
358
373
// We want to transform to topic full name in the order: domain://tenant/namespace/topic
359
- if (mlName == null || mlName .length () == 0 ) {
374
+ if (mlName == null || mlName .isEmpty () ) {
360
375
return mlName ;
361
376
}
362
- List <String > parts = Splitter . on ( "/" ). splitToList ( mlName );
377
+ List <String > parts = splitBySlash ( mlName , 0 );
363
378
String tenant ;
364
379
String cluster ;
365
380
String namespacePortion ;
@@ -370,14 +385,14 @@ public static String fromPersistenceNamingEncoding(String mlName) {
370
385
namespacePortion = parts .get (1 );
371
386
domain = parts .get (2 );
372
387
localName = Codec .decode (parts .get (3 ));
373
- return String . format ( "%s ://%s/%s/%s" , domain , tenant , namespacePortion , localName ) ;
388
+ return domain + " ://" + tenant + "/" + namespacePortion + "/" + localName ;
374
389
} else if (parts .size () == 5 ) {
375
390
tenant = parts .get (0 );
376
391
cluster = parts .get (1 );
377
392
namespacePortion = parts .get (2 );
378
393
domain = parts .get (3 );
379
394
localName = Codec .decode (parts .get (4 ));
380
- return String . format ( "%s ://%s/%s/%s/%s" , domain , tenant , cluster , namespacePortion , localName ) ;
395
+ return domain + " ://" + tenant + "/" + cluster + "/" + namespacePortion + "/" + localName ;
381
396
} else {
382
397
throw new IllegalArgumentException ("Invalid managedLedger name: " + mlName );
383
398
}
0 commit comments