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.File; 027import java.io.IOException; 028import java.io.InputStream; 029import java.io.OutputStream; 030import java.io.UncheckedIOException; 031import java.nio.ByteBuffer; 032import java.nio.charset.StandardCharsets; 033import java.nio.file.Files; 034import java.nio.file.StandardCopyOption; 035 036import org.eclipse.aether.spi.io.FileProcessor; 037import org.eclipse.aether.util.ChecksumUtils; 038import org.eclipse.aether.util.FileUtils; 039 040/** 041 * A utility class helping with file-based operations. 042 */ 043@Singleton 044@Named 045public class DefaultFileProcessor implements FileProcessor { 046 047 /** 048 * Thread-safe variant of {@link File#mkdirs()}. Creates the directory named by the given abstract pathname, 049 * including any necessary but nonexistent parent directories. Note that if this operation fails it may have 050 * succeeded in creating some of the necessary parent directories. 051 * 052 * @param directory The directory to create, may be {@code null}. 053 * @return {@code true} if and only if the directory was created, along with all necessary parent directories; 054 * {@code false} otherwise 055 */ 056 public boolean mkdirs(File directory) { 057 if (directory == null) { 058 return false; 059 } 060 061 if (directory.exists()) { 062 return false; 063 } 064 if (directory.mkdir()) { 065 return true; 066 } 067 068 File canonDir; 069 try { 070 canonDir = directory.getCanonicalFile(); 071 } catch (IOException e) { 072 throw new UncheckedIOException(e); 073 } 074 075 File parentDir = canonDir.getParentFile(); 076 return (parentDir != null && (mkdirs(parentDir) || parentDir.exists()) && canonDir.mkdir()); 077 } 078 079 public void write(File target, String data) throws IOException { 080 FileUtils.writeFile(target.toPath(), p -> Files.write(p, data.getBytes(StandardCharsets.UTF_8))); 081 } 082 083 public void write(File target, InputStream source) throws IOException { 084 FileUtils.writeFile(target.toPath(), p -> Files.copy(source, p, StandardCopyOption.REPLACE_EXISTING)); 085 } 086 087 public void copy(File source, File target) throws IOException { 088 copy(source, target, null); 089 } 090 091 public long copy(File source, File target, ProgressListener listener) throws IOException { 092 try (InputStream in = new BufferedInputStream(Files.newInputStream(source.toPath())); 093 FileUtils.CollocatedTempFile tempTarget = FileUtils.newTempFile(target.toPath()); 094 OutputStream out = new BufferedOutputStream(Files.newOutputStream(tempTarget.getPath()))) { 095 long result = copy(out, in, listener); 096 tempTarget.move(); 097 return result; 098 } 099 } 100 101 private long copy(OutputStream os, InputStream is, ProgressListener listener) throws IOException { 102 long total = 0L; 103 byte[] buffer = new byte[1024 * 32]; 104 while (true) { 105 int bytes = is.read(buffer); 106 if (bytes < 0) { 107 break; 108 } 109 110 os.write(buffer, 0, bytes); 111 112 total += bytes; 113 114 if (listener != null && bytes > 0) { 115 try { 116 listener.progressed(ByteBuffer.wrap(buffer, 0, bytes)); 117 } catch (Exception e) { 118 // too bad 119 } 120 } 121 } 122 123 return total; 124 } 125 126 public void move(File source, File target) throws IOException { 127 if (!source.renameTo(target)) { 128 copy(source, target); 129 130 target.setLastModified(source.lastModified()); 131 132 source.delete(); 133 } 134 } 135 136 @Override 137 public String readChecksum(final File checksumFile) throws IOException { 138 // for now do exactly same as happened before, but FileProcessor is a component and can be replaced 139 return ChecksumUtils.read(checksumFile); 140 } 141 142 @Override 143 public void writeChecksum(final File checksumFile, final String checksum) throws IOException { 144 // for now do exactly same as happened before, but FileProcessor is a component and can be replaced 145 write(checksumFile, checksum); 146 } 147}