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