001package org.eclipse.aether.internal.impl; 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.File; 023import java.io.FileInputStream; 024import java.io.FileOutputStream; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.OutputStream; 028import java.nio.ByteBuffer; 029import java.nio.charset.StandardCharsets; 030 031import javax.inject.Named; 032import javax.inject.Singleton; 033 034import org.eclipse.aether.spi.io.FileProcessor; 035import org.eclipse.aether.util.ChecksumUtils; 036 037/** 038 * A utility class helping with file-based operations. 039 */ 040@Singleton 041@Named 042public class DefaultFileProcessor 043 implements FileProcessor 044{ 045 046 /** 047 * Thread-safe variant of {@link File#mkdirs()}. Creates the directory named by the given abstract pathname, 048 * including any necessary but nonexistent parent directories. Note that if this operation fails it may have 049 * succeeded in creating some of the necessary parent directories. 050 * 051 * @param directory The directory to create, may be {@code null}. 052 * @return {@code true} if and only if the directory was created, along with all necessary parent directories; 053 * {@code false} otherwise 054 */ 055 public boolean mkdirs( File directory ) 056 { 057 if ( directory == null ) 058 { 059 return false; 060 } 061 062 if ( directory.exists() ) 063 { 064 return false; 065 } 066 if ( directory.mkdir() ) 067 { 068 return true; 069 } 070 071 File canonDir; 072 try 073 { 074 canonDir = directory.getCanonicalFile(); 075 } 076 catch ( IOException e ) 077 { 078 return false; 079 } 080 081 File parentDir = canonDir.getParentFile(); 082 return ( parentDir != null && ( mkdirs( parentDir ) || parentDir.exists() ) && canonDir.mkdir() ); 083 } 084 085 public void write( File target, String data ) 086 throws IOException 087 { 088 mkdirs( target.getAbsoluteFile().getParentFile() ); 089 090 OutputStream out = null; 091 try 092 { 093 out = new FileOutputStream( target ); 094 095 if ( data != null ) 096 { 097 out.write( data.getBytes( StandardCharsets.UTF_8 ) ); 098 } 099 100 out.close(); 101 out = null; 102 } 103 finally 104 { 105 try 106 { 107 if ( out != null ) 108 { 109 out.close(); 110 } 111 } 112 catch ( final IOException e ) 113 { 114 // Suppressed due to an exception already thrown in the try block. 115 } 116 } 117 } 118 119 public void write( File target, InputStream source ) 120 throws IOException 121 { 122 mkdirs( target.getAbsoluteFile().getParentFile() ); 123 124 OutputStream out = null; 125 try 126 { 127 out = new FileOutputStream( target ); 128 129 copy( out, source, null ); 130 131 out.close(); 132 out = null; 133 } 134 finally 135 { 136 try 137 { 138 if ( out != null ) 139 { 140 out.close(); 141 } 142 } 143 catch ( final IOException e ) 144 { 145 // Suppressed due to an exception already thrown in the try block. 146 } 147 } 148 } 149 150 public void copy( File source, File target ) 151 throws IOException 152 { 153 copy( source, target, null ); 154 } 155 156 public long copy( File source, File target, ProgressListener listener ) 157 throws IOException 158 { 159 long total = 0L; 160 161 InputStream in = null; 162 OutputStream out = null; 163 try 164 { 165 in = new FileInputStream( source ); 166 167 mkdirs( target.getAbsoluteFile().getParentFile() ); 168 169 out = new FileOutputStream( target ); 170 171 total = copy( out, in, listener ); 172 173 out.close(); 174 out = null; 175 176 in.close(); 177 in = null; 178 } 179 finally 180 { 181 try 182 { 183 if ( out != null ) 184 { 185 out.close(); 186 } 187 } 188 catch ( final IOException e ) 189 { 190 // Suppressed due to an exception already thrown in the try block. 191 } 192 finally 193 { 194 try 195 { 196 if ( in != null ) 197 { 198 in.close(); 199 } 200 } 201 catch ( final IOException e ) 202 { 203 // Suppressed due to an exception already thrown in the try block. 204 } 205 } 206 } 207 208 return total; 209 } 210 211 private long copy( OutputStream os, InputStream is, ProgressListener listener ) 212 throws IOException 213 { 214 long total = 0L; 215 byte[] buffer = new byte[ 1024 * 32 ]; 216 while ( true ) 217 { 218 int bytes = is.read( buffer ); 219 if ( bytes < 0 ) 220 { 221 break; 222 } 223 224 os.write( buffer, 0, bytes ); 225 226 total += bytes; 227 228 if ( listener != null && bytes > 0 ) 229 { 230 try 231 { 232 listener.progressed( ByteBuffer.wrap( buffer, 0, bytes ) ); 233 } 234 catch ( Exception e ) 235 { 236 // too bad 237 } 238 } 239 } 240 241 return total; 242 } 243 244 public void move( File source, File target ) 245 throws IOException 246 { 247 if ( !source.renameTo( target ) ) 248 { 249 copy( source, target ); 250 251 target.setLastModified( source.lastModified() ); 252 253 source.delete(); 254 } 255 } 256 257 @Override 258 public String readChecksum( final File checksumFile ) throws IOException 259 { 260 // for now do exactly same as happened before, but FileProcessor is a component and can be replaced 261 return ChecksumUtils.read( checksumFile ); 262 } 263 264 @Override 265 public void writeChecksum( final File checksumFile, final String checksum ) throws IOException 266 { 267 // for now do exactly same as happened before, but FileProcessor is a component and can be replaced 268 write( checksumFile, checksum ); 269 } 270}