001package org.apache.maven.resolver.examples.util;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 * 
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.PrintStream;
023import java.text.DecimalFormat;
024import java.text.DecimalFormatSymbols;
025import java.util.Locale;
026import java.util.Map;
027import java.util.concurrent.ConcurrentHashMap;
028
029import org.eclipse.aether.transfer.AbstractTransferListener;
030import org.eclipse.aether.transfer.MetadataNotFoundException;
031import org.eclipse.aether.transfer.TransferEvent;
032import org.eclipse.aether.transfer.TransferResource;
033
034import static java.util.Objects.requireNonNull;
035
036/**
037 * A simplistic transfer listener that logs uploads/downloads to the console.
038 */
039public class ConsoleTransferListener
040    extends AbstractTransferListener
041{
042
043    private final PrintStream out;
044
045    private final Map<TransferResource, Long> downloads = new ConcurrentHashMap<>();
046
047    private int lastLength;
048
049    public ConsoleTransferListener()
050    {
051        this( null );
052    }
053
054    public ConsoleTransferListener( PrintStream out )
055    {
056        this.out = ( out != null ) ? out : System.out;
057    }
058
059    @Override
060    public void transferInitiated( TransferEvent event )
061    {
062        requireNonNull( event, "event cannot be null" );
063        String message = event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploading" : "Downloading";
064
065        out.println( message + ": " + event.getResource().getRepositoryUrl() + event.getResource().getResourceName() );
066    }
067
068    @Override
069    public void transferProgressed( TransferEvent event )
070    {
071        requireNonNull( event, "event cannot be null" );
072        TransferResource resource = event.getResource();
073        downloads.put( resource, event.getTransferredBytes() );
074
075        StringBuilder buffer = new StringBuilder( 64 );
076
077        for ( Map.Entry<TransferResource, Long> entry : downloads.entrySet() )
078        {
079            long total = entry.getKey().getContentLength();
080            long complete = entry.getValue();
081
082            buffer.append( getStatus( complete, total ) ).append( "  " );
083        }
084
085        int pad = lastLength - buffer.length();
086        lastLength = buffer.length();
087        pad( buffer, pad );
088        buffer.append( '\r' );
089
090        out.print( buffer );
091    }
092
093    private String getStatus( long complete, long total )
094    {
095        if ( total >= 1024 )
096        {
097            return toKB( complete ) + "/" + toKB( total ) + " KB ";
098        }
099        else if ( total >= 0 )
100        {
101            return complete + "/" + total + " B ";
102        }
103        else if ( complete >= 1024 )
104        {
105            return toKB( complete ) + " KB ";
106        }
107        else
108        {
109            return complete + " B ";
110        }
111    }
112
113    private void pad( StringBuilder buffer, int spaces )
114    {
115        String block = "                                        ";
116        while ( spaces > 0 )
117        {
118            int n = Math.min( spaces, block.length() );
119            buffer.append( block, 0, n );
120            spaces -= n;
121        }
122    }
123
124    @Override
125    public void transferSucceeded( TransferEvent event )
126    {
127        requireNonNull( event, "event cannot be null" );
128        transferCompleted( event );
129
130        TransferResource resource = event.getResource();
131        long contentLength = event.getTransferredBytes();
132        if ( contentLength >= 0 )
133        {
134            String type = ( event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploaded" : "Downloaded" );
135            String len = contentLength >= 1024 ? toKB( contentLength ) + " KB" : contentLength + " B";
136
137            String throughput = "";
138            long duration = System.currentTimeMillis() - resource.getTransferStartTime();
139            if ( duration > 0 )
140            {
141                long bytes = contentLength - resource.getResumeOffset();
142                DecimalFormat format = new DecimalFormat( "0.0", new DecimalFormatSymbols( Locale.ENGLISH ) );
143                double kbPerSec = ( bytes / 1024.0 ) / ( duration / 1000.0 );
144                throughput = " at " + format.format( kbPerSec ) + " KB/sec";
145            }
146
147            out.println( type + ": " + resource.getRepositoryUrl() + resource.getResourceName() + " (" + len
148                + throughput + ")" );
149        }
150    }
151
152    @Override
153    public void transferFailed( TransferEvent event )
154    {
155        requireNonNull( event, "event cannot be null" );
156        transferCompleted( event );
157
158        if ( !( event.getException() instanceof MetadataNotFoundException ) )
159        {
160            event.getException().printStackTrace( out );
161        }
162    }
163
164    private void transferCompleted( TransferEvent event )
165    {
166        requireNonNull( event, "event cannot be null" );
167        downloads.remove( event.getResource() );
168
169        StringBuilder buffer = new StringBuilder( 64 );
170        pad( buffer, lastLength );
171        buffer.append( '\r' );
172        out.print( buffer );
173    }
174
175    public void transferCorrupted( TransferEvent event )
176    {
177        requireNonNull( event, "event cannot be null" );
178        event.getException().printStackTrace( out );
179    }
180
181    @SuppressWarnings( "checkstyle:magicnumber" )
182    protected long toKB( long bytes )
183    {
184        return ( bytes + 1023 ) / 1024;
185    }
186
187}