19
19
package org .apache .pulsar .broker .admin .v2 ;
20
20
21
21
import static org .apache .pulsar .common .util .Codec .decode ;
22
+ import static org .apache .pulsar .common .util .FutureUtil .unwrapCompletionException ;
22
23
import com .fasterxml .jackson .core .JsonProcessingException ;
23
24
import io .swagger .annotations .Api ;
24
25
import io .swagger .annotations .ApiOperation ;
25
26
import io .swagger .annotations .ApiParam ;
26
27
import io .swagger .annotations .ApiResponse ;
27
28
import io .swagger .annotations .ApiResponses ;
29
+ import java .io .BufferedOutputStream ;
28
30
import java .io .IOException ;
31
+ import java .io .OutputStream ;
29
32
import java .util .List ;
30
33
import java .util .Map ;
31
34
import java .util .Optional ;
32
35
import java .util .Set ;
36
+ import java .util .concurrent .CompletionException ;
33
37
import javax .servlet .AsyncContext ;
34
38
import javax .servlet .ServletOutputStream ;
35
39
import javax .servlet .WriteListener ;
50
54
import javax .ws .rs .core .Context ;
51
55
import javax .ws .rs .core .MediaType ;
52
56
import javax .ws .rs .core .Response ;
57
+ import javax .ws .rs .core .StreamingOutput ;
53
58
import org .apache .bookkeeper .mledger .Position ;
54
59
import org .apache .bookkeeper .mledger .impl .PositionImpl ;
55
60
import org .apache .pulsar .broker .admin .impl .PersistentTopicsBase ;
@@ -1127,7 +1132,7 @@ public void deleteTopic(
1127
1132
internalDeleteTopicAsync (authoritative , force )
1128
1133
.thenAccept (__ -> asyncResponse .resume (Response .noContent ().build ()))
1129
1134
.exceptionally (ex -> {
1130
- Throwable t = FutureUtil . unwrapCompletionException (ex );
1135
+ Throwable t = unwrapCompletionException (ex );
1131
1136
if (!force && (t instanceof BrokerServiceException .TopicBusyException )) {
1132
1137
ex = new RestException (Response .Status .PRECONDITION_FAILED ,
1133
1138
t .getMessage ());
@@ -1235,8 +1240,7 @@ public void getStats(
1235
1240
@ ApiResponse (code = 412 , message = "Topic name is not valid" ),
1236
1241
@ ApiResponse (code = 500 , message = "Internal server error" ),
1237
1242
@ ApiResponse (code = 503 , message = "Failed to validate global cluster configuration" ) })
1238
- public void getInternalStats (
1239
- @ Suspended final AsyncResponse asyncResponse ,
1243
+ public StreamingOutput getInternalStats (
1240
1244
@ ApiParam (value = "Specify the tenant" , required = true )
1241
1245
@ PathParam ("tenant" ) String tenant ,
1242
1246
@ ApiParam (value = "Specify the namespace" , required = true )
@@ -1247,54 +1251,22 @@ public void getInternalStats(
1247
1251
@ QueryParam ("authoritative" ) @ DefaultValue ("false" ) boolean authoritative ,
1248
1252
@ QueryParam ("metadata" ) @ DefaultValue ("false" ) boolean metadata ) {
1249
1253
validateTopicName (tenant , namespace , encodedTopic );
1250
- internalGetInternalStatsAsync (authoritative , metadata )
1251
- .thenAccept (stats -> {
1252
- final AsyncContext asyncContext = servletRequest .getAsyncContext ();
1253
- final ServletOutputStream s ;
1254
- try {
1255
- s = asyncContext .getResponse ().getOutputStream ();
1256
- } catch (IOException e ) {
1257
- log .error ("Cannot get the outputstream to write the response" , e );
1258
- resumeAsyncResponseExceptionally (asyncResponse , e );
1259
- return ;
1260
- }
1261
- s .setWriteListener (new WriteListener () {
1262
-
1263
- public void onWritePossible () throws IOException {
1264
- log .info ("Serializing internal stats for topic {} to outputstream" , topicName );
1265
- if (!s .isReady ()) {
1266
- return ;
1267
- }
1268
- byte [] asArray = ObjectMapperFactory .getMapper ().getObjectMapper ().writeValueAsBytes (stats );
1269
- s .write (asArray );
1270
- if (!s .isReady ()) {
1271
- return ;
1272
- }
1273
- s .close ();
1274
- if (!s .isReady ()) {
1275
- return ;
1276
- }
1277
- asyncContext .complete ();
1278
- log .info ("Finished writing internal stats for topic {} to outputstream" , topicName );
1279
-
1254
+ return output -> {
1255
+ internalGetInternalStatsAsync (authoritative , metadata )
1256
+ .thenAccept (stats -> {
1257
+ try {
1258
+ ObjectMapperFactory .getMapper ().getObjectMapper ().writeValue (output , stats );
1259
+ } catch (IOException error ) {
1260
+ throw new CompletionException (error );
1280
1261
}
1281
-
1282
- @ Override
1283
- public void onError (Throwable ex ) {
1284
- if (isNot307And404Exception (ex )) {
1285
- log .error ("[{}] Failed to server internal stats for topic {}" ,
1286
- clientAppId (), topicName , ex );
1287
- }
1262
+ })
1263
+ .exceptionally (ex -> {
1264
+ if (isNot307And404Exception (ex )) {
1265
+ log .error ("[{}] Failed to get internal stats for topic {}" , clientAppId (), topicName , ex );
1288
1266
}
1267
+ throw translateToWebApplicationException (ex );
1289
1268
});
1290
- })
1291
- .exceptionally (ex -> {
1292
- if (isNot307And404Exception (ex )) {
1293
- log .error ("[{}] Failed to get internal stats for topic {}" , clientAppId (), topicName , ex );
1294
- }
1295
- resumeAsyncResponseExceptionally (asyncResponse , ex );
1296
- return null ;
1297
- });
1269
+ };
1298
1270
}
1299
1271
1300
1272
@ GET
@@ -1435,7 +1407,7 @@ public void deleteSubscription(
1435
1407
internalDeleteSubscriptionAsync (subName , authoritative , force )
1436
1408
.thenRun (() -> asyncResponse .resume (Response .noContent ().build ()))
1437
1409
.exceptionally (ex -> {
1438
- Throwable cause = FutureUtil . unwrapCompletionException (ex );
1410
+ Throwable cause = unwrapCompletionException (ex );
1439
1411
1440
1412
// If the exception is not redirect exception we need to log it.
1441
1413
if (!isRedirectException (cause )) {
@@ -1727,7 +1699,7 @@ public void resetCursor(
1727
1699
internalResetCursorAsync (decode (encodedSubName ), timestamp , authoritative )
1728
1700
.thenAccept (__ -> asyncResponse .resume (Response .noContent ().build ()))
1729
1701
.exceptionally (ex -> {
1730
- Throwable t = FutureUtil . unwrapCompletionException (ex );
1702
+ Throwable t = unwrapCompletionException (ex );
1731
1703
if (!isRedirectException (t )) {
1732
1704
log .error ("[{}][{}] Failed to reset cursor on subscription {} to time {}" ,
1733
1705
clientAppId (), topicName , encodedSubName , timestamp , t );
@@ -2093,7 +2065,7 @@ public void getBacklog(
2093
2065
.thenCompose (__ -> internalGetBacklogAsync (authoritative ))
2094
2066
.thenAccept (asyncResponse ::resume )
2095
2067
.exceptionally (ex -> {
2096
- Throwable t = FutureUtil . unwrapCompletionException (ex );
2068
+ Throwable t = unwrapCompletionException (ex );
2097
2069
if (t instanceof MetadataStoreException .NotFoundException ) {
2098
2070
log .warn ("[{}] Failed to get topic backlog {}: Namespace does not exist" , clientAppId (),
2099
2071
namespaceName );
@@ -4247,7 +4219,7 @@ public void truncateTopic(
4247
4219
internalTruncateTopicAsync (authoritative )
4248
4220
.thenAccept (__ -> asyncResponse .resume (Response .noContent ().build ()))
4249
4221
.exceptionally (ex -> {
4250
- Throwable t = FutureUtil . unwrapCompletionException (ex );
4222
+ Throwable t = unwrapCompletionException (ex );
4251
4223
if (!isRedirectException (t )) {
4252
4224
log .error ("[{}] Failed to truncate topic {}" , clientAppId (), topicName , t );
4253
4225
}
0 commit comments