2121import com .fasterxml .jackson .annotation .PropertyAccessor ;
2222import com .fasterxml .jackson .databind .ObjectMapper ;
2323import com .fasterxml .jackson .databind .ObjectWriter ;
24+ import com .fasterxml .jackson .databind .SequenceWriter ;
2425import com .fasterxml .jackson .databind .SerializationFeature ;
2526import com .fasterxml .jackson .datatype .jsr310 .JavaTimeModule ;
2627import com .google .common .base .Strings ;
2728import java .io .IOException ;
29+ import java .io .OutputStream ;
30+ import java .util .List ;
2831import org .apache .hadoop .hdds .cli .HddsVersionProvider ;
2932import org .apache .hadoop .hdds .client .ReplicationConfig ;
3033import org .apache .hadoop .hdds .client .ReplicationType ;
4043import picocli .CommandLine .Option ;
4144
4245/**
43- * This is the handler that process container list command.
46+ * The ListSubcommand class represents a command to list containers in a structured way.
47+ * It provides options to control how the list is generated, including specifying
48+ * starting container ID, maximum number of containers to list, and other filtering criteria
49+ * such as container state or replication type.
50+ *
51+ * This command connects to the SCM (Storage Container Manager) client to fetch the
52+ * container details and outputs the result in a JSON format.
4453 */
4554@ Command (
4655 name = "list" ,
@@ -89,13 +98,6 @@ public class ListSubcommand extends ScmSubcommand {
8998 WRITER = mapper .writerWithDefaultPrettyPrinter ();
9099 }
91100
92-
93- private void outputContainerInfo (ContainerInfo containerInfo )
94- throws IOException {
95- // Print container report info.
96- System .out .println (WRITER .writeValueAsString (containerInfo ));
97- }
98-
99101 @ Override
100102 public void execute (ScmClient scmClient ) throws IOException {
101103 if (!Strings .isNullOrEmpty (replication ) && type == null ) {
@@ -114,44 +116,104 @@ public void execute(ScmClient scmClient) throws IOException {
114116 .getInt (ScmConfigKeys .OZONE_SCM_CONTAINER_LIST_MAX_COUNT ,
115117 ScmConfigKeys .OZONE_SCM_CONTAINER_LIST_MAX_COUNT_DEFAULT );
116118
117- ContainerListResult containerListAndTotalCount ;
119+ // Use SequenceWriter to output JSON array format for all cases
120+ SequenceWriter sequenceWriter = WRITER .writeValues (new NonClosingOutputStream (System .out ));
121+ sequenceWriter .init (true ); // Initialize as a JSON array
118122
119123 if (!all ) {
124+ // Regular listing with count limit
120125 if (count > maxCountAllowed ) {
121126 System .err .printf ("Attempting to list the first %d records of containers." +
122127 " However it exceeds the cluster's current limit of %d. The results will be capped at the" +
123- " maximum allowed count.%n" , count , ScmConfigKeys . OZONE_SCM_CONTAINER_LIST_MAX_COUNT_DEFAULT );
128+ " maximum allowed count.%n" , count , maxCountAllowed );
124129 count = maxCountAllowed ;
125130 }
126- containerListAndTotalCount = scmClient .listContainer (startId , count , state , type , repConfig );
127- for (ContainerInfo container : containerListAndTotalCount .getContainerInfoList ()) {
128- outputContainerInfo (container );
129- }
130131
131- if (containerListAndTotalCount .getTotalCount () > count ) {
132+ ContainerListResult containerListResult =
133+ scmClient .listContainer (startId , count , state , type , repConfig );
134+
135+ writeContainers (sequenceWriter , containerListResult .getContainerInfoList ());
136+
137+ closeStream (sequenceWriter );
138+ if (containerListResult .getTotalCount () > count ) {
132139 System .err .printf ("Displaying %d out of %d containers. " +
133- "Container list has more containers.%n" ,
134- count , containerListAndTotalCount .getTotalCount ());
140+ "Container list has more containers.%n" ,
141+ count , containerListResult .getTotalCount ());
135142 }
136143 } else {
137- // Batch size is either count passed through cli or maxCountAllowed
144+ // List all containers by fetching in batches
138145 int batchSize = (count > 0 ) ? count : maxCountAllowed ;
139- long currentStartId = startId ;
140- int fetchedCount ;
141-
142- do {
143- // Fetch containers in batches of 'batchSize'
144- containerListAndTotalCount = scmClient .listContainer (currentStartId , batchSize , state , type , repConfig );
145- fetchedCount = containerListAndTotalCount .getContainerInfoList ().size ();
146-
147- for (ContainerInfo container : containerListAndTotalCount .getContainerInfoList ()) {
148- outputContainerInfo (container );
149- }
150-
151- if (fetchedCount > 0 ) {
152- currentStartId = containerListAndTotalCount .getContainerInfoList ().get (fetchedCount - 1 ).getContainerID () + 1 ;
153- }
154- } while (fetchedCount > 0 );
146+ listAllContainers (scmClient , sequenceWriter , batchSize , repConfig );
147+ closeStream (sequenceWriter );
148+ }
149+ }
150+
151+ private void writeContainers (SequenceWriter writer , List <ContainerInfo > containers )
152+ throws IOException {
153+ for (ContainerInfo container : containers ) {
154+ writer .write (container );
155+ }
156+ }
157+
158+ private void closeStream (SequenceWriter writer ) throws IOException {
159+ writer .flush ();
160+ writer .close ();
161+ // Add the final newline
162+ System .out .println ();
163+ }
164+
165+ private void listAllContainers (ScmClient scmClient , SequenceWriter writer ,
166+ int batchSize , ReplicationConfig repConfig )
167+ throws IOException {
168+ long currentStartId = startId ;
169+ int fetchedCount ;
170+
171+ do {
172+ ContainerListResult result =
173+ scmClient .listContainer (currentStartId , batchSize , state , type , repConfig );
174+ fetchedCount = result .getContainerInfoList ().size ();
175+
176+ writeContainers (writer , result .getContainerInfoList ());
177+
178+ if (fetchedCount > 0 ) {
179+ currentStartId =
180+ result .getContainerInfoList ().get (fetchedCount - 1 ).getContainerID () + 1 ;
181+ }
182+ } while (fetchedCount > 0 );
183+ }
184+
185+
186+ private static class NonClosingOutputStream extends OutputStream {
187+
188+ private final OutputStream delegate ;
189+
190+ NonClosingOutputStream (OutputStream delegate ) {
191+ this .delegate = delegate ;
192+ }
193+
194+ @ Override
195+ public void write (int b ) throws IOException {
196+ delegate .write (b );
197+ }
198+
199+ @ Override
200+ public void write (byte [] b ) throws IOException {
201+ delegate .write (b );
202+ }
203+
204+ @ Override
205+ public void write (byte [] b , int off , int len ) throws IOException {
206+ delegate .write (b , off , len );
207+ }
208+
209+ @ Override
210+ public void flush () throws IOException {
211+ delegate .flush ();
212+ }
213+
214+ @ Override
215+ public void close () {
216+ // Ignore close to keep the underlying stream open
155217 }
156218 }
157219}
0 commit comments