001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.impl;
020
021import javax.inject.Named;
022import javax.inject.Singleton;
023
024import java.io.BufferedInputStream;
025import java.io.BufferedOutputStream;
026import java.io.IOException;
027import java.io.InputStream;
028import java.io.OutputStream;
029import java.nio.ByteBuffer;
030import java.nio.charset.StandardCharsets;
031import java.nio.file.AccessDeniedException;
032import java.nio.file.FileSystemException;
033import java.nio.file.Files;
034import java.nio.file.Path;
035import java.nio.file.StandardCopyOption;
036import java.nio.file.attribute.FileTime;
037
038import org.eclipse.aether.spi.io.PathProcessor;
039import org.eclipse.aether.util.FileUtils;
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042
043/**
044 * A utility class helping with file-based operations.
045 */
046@Singleton
047@Named
048public class DefaultPathProcessor implements PathProcessor {
049    private final Logger logger = LoggerFactory.getLogger(getClass());
050
051    @Override
052    public void setLastModified(Path path, long value) throws IOException {
053        try {
054            Files.setLastModifiedTime(path, FileTime.fromMillis(value));
055        } catch (FileSystemException e) {
056            // MRESOLVER-536: Java uses generic FileSystemException for some weird cases,
057            // but some subclasses like AccessDeniedEx should be re-thrown
058            if (e instanceof AccessDeniedException) {
059                throw e;
060            }
061            logger.trace("Failed to set last modified date: {}", path, e);
062        }
063    }
064
065    @Override
066    public void write(Path target, String data) throws IOException {
067        FileUtils.writeFile(target, p -> Files.write(p, data.getBytes(StandardCharsets.UTF_8)));
068    }
069
070    @Override
071    public void write(Path target, InputStream source) throws IOException {
072        FileUtils.writeFile(target, p -> Files.copy(source, p, StandardCopyOption.REPLACE_EXISTING));
073    }
074
075    @Override
076    public long copy(Path source, Path target, ProgressListener listener) throws IOException {
077        try (InputStream in = new BufferedInputStream(Files.newInputStream(source));
078                FileUtils.CollocatedTempFile tempTarget = FileUtils.newTempFile(target);
079                OutputStream out = new BufferedOutputStream(Files.newOutputStream(tempTarget.getPath()))) {
080            long result = copy(out, in, listener);
081            tempTarget.move();
082            return result;
083        }
084    }
085
086    private long copy(OutputStream os, InputStream is, ProgressListener listener) throws IOException {
087        long total = 0L;
088        byte[] buffer = new byte[1024 * 32];
089        while (true) {
090            int bytes = is.read(buffer);
091            if (bytes < 0) {
092                break;
093            }
094
095            os.write(buffer, 0, bytes);
096
097            total += bytes;
098
099            if (listener != null && bytes > 0) {
100                try {
101                    listener.progressed(ByteBuffer.wrap(buffer, 0, bytes));
102                } catch (Exception e) {
103                    // too bad
104                }
105            }
106        }
107
108        return total;
109    }
110
111    @Override
112    public void move(Path source, Path target) throws IOException {
113        Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
114    }
115}