View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.cli.transfer;
20  
21  import java.io.PrintStream;
22  import java.util.Collections;
23  import java.util.Iterator;
24  import java.util.LinkedHashMap;
25  import java.util.Locale;
26  import java.util.Map;
27  
28  import org.eclipse.aether.transfer.TransferCancelledException;
29  import org.eclipse.aether.transfer.TransferEvent;
30  import org.eclipse.aether.transfer.TransferResource;
31  
32  /**
33   * Console download progress meter.
34   *
35   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
36   */
37  public class ConsoleMavenTransferListener extends AbstractMavenTransferListener {
38  
39      private final Map<TransferResourceIdentifier, TransferResourceAndSize> transfers =
40              Collections.synchronizedMap(new LinkedHashMap<TransferResourceIdentifier, TransferResourceAndSize>());
41  
42      private final boolean printResourceNames;
43      private int lastLength;
44  
45      public ConsoleMavenTransferListener(PrintStream out, boolean printResourceNames) {
46          super(out);
47          this.printResourceNames = printResourceNames;
48      }
49  
50      @Override
51      public void transferInitiated(TransferEvent event) {
52          overridePreviousTransfer(event);
53  
54          super.transferInitiated(event);
55      }
56  
57      @Override
58      public void transferCorrupted(TransferEvent event) throws TransferCancelledException {
59          overridePreviousTransfer(event);
60  
61          super.transferCorrupted(event);
62      }
63  
64      @Override
65      public void transferProgressed(TransferEvent event) throws TransferCancelledException {
66          TransferResource resource = event.getResource();
67          transfers.put(
68                  new TransferResourceIdentifier(resource),
69                  new TransferResourceAndSize(resource, event.getTransferredBytes()));
70  
71          StringBuilder buffer = new StringBuilder(128);
72          buffer.append("Progress (").append(transfers.size()).append("): ");
73  
74          synchronized (transfers) {
75              Iterator<TransferResourceAndSize> entries = transfers.values().iterator();
76              while (entries.hasNext()) {
77                  TransferResourceAndSize entry = entries.next();
78                  long total = entry.resource.getContentLength();
79                  Long complete = entry.transferredBytes;
80                  buffer.append(getStatus(entry.resource.getResourceName(), complete, total));
81                  if (entries.hasNext()) {
82                      buffer.append(" | ");
83                  }
84              }
85          }
86  
87          int pad = lastLength - buffer.length();
88          lastLength = buffer.length();
89          pad(buffer, pad);
90          buffer.append('\r');
91          out.print(buffer);
92          out.flush();
93      }
94  
95      private String getStatus(String resourceName, long complete, long total) {
96          FileSizeFormat format = new FileSizeFormat(Locale.ENGLISH);
97          StringBuilder status = new StringBuilder();
98  
99          if (printResourceNames) {
100             status.append(resourceName(resourceName));
101             status.append(" (");
102         }
103 
104         status.append(format.formatProgress(complete, total));
105 
106         if (printResourceNames) {
107             status.append(")");
108         }
109 
110         return status.toString();
111     }
112 
113     private String resourceName(String resourceName) {
114         if (resourceName == null || resourceName.trim().isEmpty()) {
115             return "";
116         }
117         final int pos = resourceName.lastIndexOf("/");
118         if (pos == -1 || pos == resourceName.length() - 1) {
119             return "";
120         }
121         return resourceName.substring(pos + 1);
122     }
123 
124     private void pad(StringBuilder buffer, int spaces) {
125         String block = "                                        ";
126         while (spaces > 0) {
127             int n = Math.min(spaces, block.length());
128             buffer.append(block, 0, n);
129             spaces -= n;
130         }
131     }
132 
133     @Override
134     public void transferSucceeded(TransferEvent event) {
135         transfers.remove(new TransferResourceIdentifier(event.getResource()));
136         overridePreviousTransfer(event);
137 
138         super.transferSucceeded(event);
139     }
140 
141     @Override
142     public void transferFailed(TransferEvent event) {
143         transfers.remove(new TransferResourceIdentifier(event.getResource()));
144         overridePreviousTransfer(event);
145 
146         super.transferFailed(event);
147     }
148 
149     private void overridePreviousTransfer(TransferEvent event) {
150         if (lastLength > 0) {
151             StringBuilder buffer = new StringBuilder(128);
152             pad(buffer, lastLength);
153             buffer.append('\r');
154             out.print(buffer);
155             out.flush();
156             lastLength = 0;
157         }
158     }
159 
160     private final class TransferResourceAndSize {
161 
162         private final TransferResource resource;
163         private final long transferredBytes;
164 
165         private TransferResourceAndSize(TransferResource resource, long transferredBytes) {
166             this.resource = resource;
167             this.transferredBytes = transferredBytes;
168         }
169     }
170 }