| package org.eclipse.aether.spi.connector.transport; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.net.URI; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * A task to download a resource from the remote repository. |
| * |
| * @see Transporter#get(GetTask) |
| */ |
| public final class GetTask |
| extends TransportTask |
| { |
| |
| private File dataFile; |
| |
| private boolean resume; |
| |
| private ByteArrayOutputStream dataBytes; |
| |
| private Map<String, String> checksums; |
| |
| /** |
| * Creates a new task for the specified remote resource. |
| * |
| * @param location The relative location of the resource in the remote repository, must not be {@code null}. |
| */ |
| public GetTask( URI location ) |
| { |
| checksums = Collections.emptyMap(); |
| setLocation( location ); |
| } |
| |
| /** |
| * Opens an output stream to store the downloaded data. Depending on {@link #getDataFile()}, this stream writes |
| * either to a file on disk or a growable buffer in memory. It's the responsibility of the caller to close the |
| * provided stream. |
| * |
| * @return The output stream for the data, never {@code null}. The stream is unbuffered. |
| * @throws IOException If the stream could not be opened. |
| */ |
| public OutputStream newOutputStream() |
| throws IOException |
| { |
| return newOutputStream( false ); |
| } |
| |
| /** |
| * Opens an output stream to store the downloaded data. Depending on {@link #getDataFile()}, this stream writes |
| * either to a file on disk or a growable buffer in memory. It's the responsibility of the caller to close the |
| * provided stream. |
| * |
| * @param resume {@code true} if the download resumes from the byte offset given by {@link #getResumeOffset()}, |
| * {@code false} if the download starts at the first byte of the resource. |
| * @return The output stream for the data, never {@code null}. The stream is unbuffered. |
| * @throws IOException If the stream could not be opened. |
| */ |
| public OutputStream newOutputStream( boolean resume ) |
| throws IOException |
| { |
| if ( dataFile != null ) |
| { |
| return new FileOutputStream( dataFile, this.resume && resume ); |
| } |
| if ( dataBytes == null ) |
| { |
| dataBytes = new ByteArrayOutputStream( 1024 ); |
| } |
| else if ( !resume ) |
| { |
| dataBytes.reset(); |
| } |
| return dataBytes; |
| } |
| |
| /** |
| * Gets the file (if any) where the downloaded data should be stored. If the specified file already exists, it will |
| * be overwritten. |
| * |
| * @return The data file or {@code null} if the data will be buffered in memory. |
| */ |
| public File getDataFile() |
| { |
| return dataFile; |
| } |
| |
| /** |
| * Sets the file where the downloaded data should be stored. If the specified file already exists, it will be |
| * overwritten. Unless the caller can reasonably expect the resource to be small, use of a data file is strongly |
| * recommended to avoid exhausting heap memory during the download. |
| * |
| * @param dataFile The file to store the downloaded data, may be {@code null} to store the data in memory. |
| * @return This task for chaining, never {@code null}. |
| */ |
| public GetTask setDataFile( File dataFile ) |
| { |
| return setDataFile( dataFile, false ); |
| } |
| |
| /** |
| * Sets the file where the downloaded data should be stored. If the specified file already exists, it will be |
| * overwritten or appended to, depending on the {@code resume} argument and the capabilities of the transporter. |
| * Unless the caller can reasonably expect the resource to be small, use of a data file is strongly recommended to |
| * avoid exhausting heap memory during the download. |
| * |
| * @param dataFile The file to store the downloaded data, may be {@code null} to store the data in memory. |
| * @param resume {@code true} to request resuming a previous download attempt, starting from the current length of |
| * the data file, {@code false} to download the resource from its beginning. |
| * @return This task for chaining, never {@code null}. |
| */ |
| public GetTask setDataFile( File dataFile, boolean resume ) |
| { |
| this.dataFile = dataFile; |
| this.resume = resume; |
| return this; |
| } |
| |
| /** |
| * Gets the byte offset within the resource from which the download should resume if supported. |
| * |
| * @return The zero-based index of the first byte to download or {@code 0} for a full download from the start of the |
| * resource, never negative. |
| */ |
| public long getResumeOffset() |
| { |
| if ( resume ) |
| { |
| if ( dataFile != null ) |
| { |
| return dataFile.length(); |
| } |
| if ( dataBytes != null ) |
| { |
| return dataBytes.size(); |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * Gets the data that was downloaded into memory. <strong>Note:</strong> This method may only be called if |
| * {@link #getDataFile()} is {@code null} as otherwise the downloaded data has been written directly to disk. |
| * |
| * @return The possibly empty data bytes, never {@code null}. |
| */ |
| public byte[] getDataBytes() |
| { |
| if ( dataFile != null || dataBytes == null ) |
| { |
| return EMPTY; |
| } |
| return dataBytes.toByteArray(); |
| } |
| |
| /** |
| * Gets the data that was downloaded into memory as a string. The downloaded data is assumed to be encoded using |
| * UTF-8. <strong>Note:</strong> This method may only be called if {@link #getDataFile()} is {@code null} as |
| * otherwise the downloaded data has been written directly to disk. |
| * |
| * @return The possibly empty data string, never {@code null}. |
| */ |
| public String getDataString() |
| { |
| if ( dataFile != null || dataBytes == null ) |
| { |
| return ""; |
| } |
| try |
| { |
| return dataBytes.toString( "UTF-8" ); |
| } |
| catch ( UnsupportedEncodingException e ) |
| { |
| throw new IllegalStateException( e ); |
| } |
| } |
| |
| /** |
| * Sets the listener that is to be notified during the transfer. |
| * |
| * @param listener The listener to notify of progress, may be {@code null}. |
| * @return This task for chaining, never {@code null}. |
| */ |
| public GetTask setListener( TransportListener listener ) |
| { |
| super.setListener( listener ); |
| return this; |
| } |
| |
| /** |
| * Gets the checksums which the remote repository advertises for the resource. The map is keyed by algorithm name |
| * (cf. {@link java.security.MessageDigest#getInstance(String)}) and the values are hexadecimal representations of |
| * the corresponding value. <em>Note:</em> This is optional data that a transporter may return if the underlying |
| * transport protocol provides metadata (e.g. HTTP headers) along with the actual resource data. |
| * |
| * @return The (read-only) checksums advertised for the downloaded resource, possibly empty but never {@code null}. |
| */ |
| public Map<String, String> getChecksums() |
| { |
| return checksums; |
| } |
| |
| /** |
| * Sets a checksum which the remote repository advertises for the resource. <em>Note:</em> Transporters should only |
| * use this method to record checksum information which is readily available while performing the actual download, |
| * they should not perform additional transfers to gather this data. |
| * |
| * @param algorithm The name of the checksum algorithm (e.g. {@code "SHA-1"}, cf. |
| * {@link java.security.MessageDigest#getInstance(String)} ), may be {@code null}. |
| * @param value The hexadecimal representation of the checksum, may be {@code null}. |
| * @return This task for chaining, never {@code null}. |
| */ |
| public GetTask setChecksum( String algorithm, String value ) |
| { |
| if ( algorithm != null ) |
| { |
| if ( checksums.isEmpty() ) |
| { |
| checksums = new HashMap<String, String>(); |
| } |
| if ( value != null && value.length() > 0 ) |
| { |
| checksums.put( algorithm, value ); |
| } |
| else |
| { |
| checksums.remove( algorithm ); |
| } |
| } |
| return this; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "<< " + getLocation(); |
| } |
| |
| } |