View Javadoc
1   package org.apache.maven.cli.transfer;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.PrintStream;
23  import java.text.DecimalFormat;
24  import java.text.DecimalFormatSymbols;
25  import java.util.Locale;
26  
27  import org.apache.commons.lang3.Validate;
28  import org.apache.maven.shared.utils.logging.MessageUtils;
29  import org.eclipse.aether.transfer.AbstractTransferListener;
30  import org.eclipse.aether.transfer.TransferCancelledException;
31  import org.eclipse.aether.transfer.TransferEvent;
32  import org.eclipse.aether.transfer.TransferResource;
33  
34  /**
35   * AbstractMavenTransferListener
36   */
37  public abstract class AbstractMavenTransferListener
38      extends AbstractTransferListener
39  {
40  
41      private static final String ESC = "\u001B";
42      private static final String ANSI_DARK_SET = ESC + "[90m";
43      private static final String ANSI_DARK_RESET = ESC + "[0m";
44  
45      // CHECKSTYLE_OFF: LineLength
46      /**
47       * Formats file size with the associated <a href="https://en.wikipedia.org/wiki/Metric_prefix">SI</a> prefix
48       * (GB, MB, kB) and using the patterns <code>#0.0</code> for numbers between 1 and 10
49       * and <code>###0</code> for numbers between 10 and 1000+ by default.
50       *
51       * @see <a href="https://en.wikipedia.org/wiki/Metric_prefix">https://en.wikipedia.org/wiki/Metric_prefix</a>
52       * @see <a href="https://en.wikipedia.org/wiki/Binary_prefix">https://en.wikipedia.org/wiki/Binary_prefix</a>
53       * @see <a
54       *      href="https://en.wikipedia.org/wiki/Octet_%28computing%29">https://en.wikipedia.org/wiki/Octet_(computing)</a>
55       */
56      // CHECKSTYLE_ON: LineLength
57      // TODO Move me to Maven Shared Utils
58      static class FileSizeFormat
59      {
60          enum ScaleUnit
61          {
62              BYTE
63              {
64                  @Override
65                  public long bytes()
66                  {
67                      return 1L;
68                  }
69  
70                  @Override
71                  public String symbol()
72                  {
73                      return "B";
74                  }
75              },
76              KILOBYTE
77              {
78                  @Override
79                  public long bytes()
80                  {
81                      return 1000L;
82                  }
83  
84                  @Override
85                  public String symbol()
86                  {
87                      return "kB";
88                  }
89              },
90              MEGABYTE
91              {
92                  @Override
93                  public long bytes()
94                  {
95                      return KILOBYTE.bytes() * KILOBYTE.bytes();
96                  }
97  
98                  @Override
99                  public String symbol()
100                 {
101                     return "MB";
102                 }
103             },
104             GIGABYTE
105             {
106                 @Override
107                 public long bytes()
108                 {
109                     return MEGABYTE.bytes() * KILOBYTE.bytes();
110                 };
111 
112                 @Override
113                 public String symbol()
114                 {
115                     return "GB";
116                 }
117             };
118 
119             public abstract long bytes();
120             public abstract String symbol();
121 
122             public static ScaleUnit getScaleUnit( long size )
123             {
124                 Validate.isTrue( size >= 0L, "file size cannot be negative: %s", size );
125 
126                 if ( size >= GIGABYTE.bytes() )
127                 {
128                     return GIGABYTE;
129                 }
130                 else if ( size >= MEGABYTE.bytes() )
131                 {
132                     return MEGABYTE;
133                 }
134                 else if ( size >= KILOBYTE.bytes() )
135                 {
136                     return KILOBYTE;
137                 }
138                 else
139                 {
140                     return BYTE;
141                 }
142             }
143         }
144 
145         private DecimalFormat smallFormat;
146         private DecimalFormat largeFormat;
147 
148         FileSizeFormat( Locale locale )
149         {
150             smallFormat = new DecimalFormat( "#0.0", new DecimalFormatSymbols( locale ) );
151             largeFormat = new DecimalFormat( "###0", new DecimalFormatSymbols( locale ) );
152         }
153 
154         public String format( long size )
155         {
156             return format( size, null );
157         }
158 
159         public String format( long size, ScaleUnit unit )
160         {
161             return format( size, unit, false );
162         }
163 
164         @SuppressWarnings( "checkstyle:magicnumber" )
165         public String format( long size, ScaleUnit unit, boolean omitSymbol )
166         {
167             Validate.isTrue( size >= 0L, "file size cannot be negative: %s", size );
168 
169             if ( unit == null )
170             {
171                 unit = ScaleUnit.getScaleUnit( size );
172             }
173 
174             double scaledSize = (double) size / unit.bytes();
175             String scaledSymbol = " " + unit.symbol();
176 
177             if ( omitSymbol )
178             {
179                 scaledSymbol = "";
180             }
181 
182             if ( unit == ScaleUnit.BYTE )
183             {
184                 return largeFormat.format( size ) + scaledSymbol;
185             }
186 
187             if ( scaledSize < 0.05 || scaledSize >= 10.0 )
188             {
189                 return largeFormat.format( scaledSize ) + scaledSymbol;
190             }
191             else
192             {
193                 return smallFormat.format( scaledSize ) + scaledSymbol;
194             }
195         }
196 
197         public String formatProgress( long progressedSize, long size )
198         {
199             Validate.isTrue( progressedSize >= 0L, "progressed file size cannot be negative: %s", progressedSize );
200             Validate.isTrue( size >= 0L && progressedSize <= size || size < 0L,
201                 "progressed file size cannot be greater than size: %s > %s", progressedSize, size );
202 
203             if ( size >= 0L && progressedSize != size )
204             {
205                 ScaleUnit unit = ScaleUnit.getScaleUnit( size );
206                 String formattedProgressedSize = format( progressedSize, unit, true );
207                 String formattedSize = format( size, unit );
208 
209                 return formattedProgressedSize + "/" + formattedSize;
210             }
211             else
212             {
213                 return format( progressedSize );
214             }
215         }
216     }
217 
218     protected PrintStream out;
219 
220     protected AbstractMavenTransferListener( PrintStream out )
221     {
222         this.out = out;
223     }
224 
225     @Override
226     public void transferInitiated( TransferEvent event )
227     {
228         String darkOn = MessageUtils.isColorEnabled() ? ANSI_DARK_SET : "";
229         String darkOff = MessageUtils.isColorEnabled() ? ANSI_DARK_RESET : "";
230 
231         String action = event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploading" : "Downloading";
232         String direction = event.getRequestType() == TransferEvent.RequestType.PUT ? "to" : "from";
233 
234         TransferResource resource = event.getResource();
235         StringBuilder message = new StringBuilder();
236         message.append( darkOn ).append( action ).append( ' ' ).append( direction ).append( ' ' );
237         message.append( darkOff ).append( resource.getRepositoryId() );
238         message.append( darkOn ).append( ": " ).append( resource.getRepositoryUrl() );
239         message.append( darkOff ).append( resource.getResourceName() );
240 
241         out.println( message.toString() );
242     }
243 
244     @Override
245     public void transferCorrupted( TransferEvent event )
246         throws TransferCancelledException
247     {
248         TransferResource resource = event.getResource();
249         // TODO This needs to be colorized
250         out.println( "[WARNING] " + event.getException().getMessage() + " from " + resource.getRepositoryId() + " for "
251             + resource.getRepositoryUrl() + resource.getResourceName() );
252     }
253 
254     @Override
255     public void transferSucceeded( TransferEvent event )
256     {
257         String darkOn = MessageUtils.isColorEnabled() ? ANSI_DARK_SET : "";
258         String darkOff = MessageUtils.isColorEnabled() ? ANSI_DARK_RESET : "";
259 
260         String action = event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploaded" : "Downloaded";
261         String direction = event.getRequestType() == TransferEvent.RequestType.PUT ? "to" : "from";
262 
263         TransferResource resource = event.getResource();
264         long contentLength = event.getTransferredBytes();
265         FileSizeFormat format = new FileSizeFormat( Locale.ENGLISH );
266 
267         StringBuilder message = new StringBuilder();
268         message.append( action ).append( darkOn ).append( ' ' ).append( direction ).append( ' ' );
269         message.append( darkOff ).append( resource.getRepositoryId() );
270         message.append( darkOn ).append(  ": " ).append( resource.getRepositoryUrl() );
271         message.append( darkOff ).append( resource.getResourceName() );
272         message.append( darkOn ).append( " (" ).append( format.format( contentLength ) );
273 
274         long duration = System.currentTimeMillis() - resource.getTransferStartTime();
275         if ( duration > 0L )
276         {
277             double bytesPerSecond = contentLength / ( duration / 1000.0 );
278             message.append( " at " ).append( format.format( (long) bytesPerSecond ) ).append( "/s" );
279         }
280 
281         message.append( ')' ).append( darkOff );
282         out.println( message.toString() );
283     }
284 
285 }