View Javadoc
1   package org.eclipse.aether.internal.impl;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.BufferedInputStream;
26  import java.io.BufferedOutputStream;
27  import java.io.File;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.OutputStream;
31  import java.io.UncheckedIOException;
32  import java.nio.ByteBuffer;
33  import java.nio.charset.StandardCharsets;
34  import java.nio.file.Files;
35  
36  import org.eclipse.aether.spi.io.FileProcessor;
37  import org.eclipse.aether.util.ChecksumUtils;
38  import org.eclipse.aether.util.FileUtils;
39  
40  /**
41   * A utility class helping with file-based operations.
42   */
43  @Singleton
44  @Named
45  public class DefaultFileProcessor
46          implements FileProcessor
47  {
48  
49      /**
50       * Thread-safe variant of {@link File#mkdirs()}. Creates the directory named by the given abstract pathname,
51       * including any necessary but nonexistent parent directories. Note that if this operation fails it may have
52       * succeeded in creating some of the necessary parent directories.
53       *
54       * @param directory The directory to create, may be {@code null}.
55       * @return {@code true} if and only if the directory was created, along with all necessary parent directories;
56       * {@code false} otherwise
57       */
58      public boolean mkdirs( File directory )
59      {
60          if ( directory == null )
61          {
62              return false;
63          }
64  
65          if ( directory.exists() )
66          {
67              return false;
68          }
69          if ( directory.mkdir() )
70          {
71              return true;
72          }
73  
74          File canonDir;
75          try
76          {
77              canonDir = directory.getCanonicalFile();
78          }
79          catch ( IOException e )
80          {
81              throw new UncheckedIOException( e );
82          }
83  
84          File parentDir = canonDir.getParentFile();
85          return ( parentDir != null && ( mkdirs( parentDir ) || parentDir.exists() ) && canonDir.mkdir() );
86      }
87  
88      public void write( File target, String data )
89              throws IOException
90      {
91          FileUtils.writeFile( target.toPath(), p -> Files.write( p, data.getBytes( StandardCharsets.UTF_8 ) ) );
92      }
93  
94      public void write( File target, InputStream source )
95              throws IOException
96      {
97          FileUtils.writeFile( target.toPath(), p -> Files.copy( source, p ) );
98      }
99  
100     public void copy( File source, File target )
101             throws IOException
102     {
103         copy( source, target, null );
104     }
105 
106     public long copy( File source, File target, ProgressListener listener )
107             throws IOException
108     {
109         try ( InputStream in = new BufferedInputStream( Files.newInputStream( source.toPath() ) );
110               FileUtils.CollocatedTempFile tempTarget = FileUtils.newTempFile( target.toPath() );
111               OutputStream out = new BufferedOutputStream( Files.newOutputStream( tempTarget.getPath() ) ) )
112         {
113             long result = copy( out, in, listener );
114             tempTarget.move();
115             return result;
116         }
117     }
118 
119     private long copy( OutputStream os, InputStream is, ProgressListener listener )
120             throws IOException
121     {
122         long total = 0L;
123         byte[] buffer = new byte[1024 * 32];
124         while ( true )
125         {
126             int bytes = is.read( buffer );
127             if ( bytes < 0 )
128             {
129                 break;
130             }
131 
132             os.write( buffer, 0, bytes );
133 
134             total += bytes;
135 
136             if ( listener != null && bytes > 0 )
137             {
138                 try
139                 {
140                     listener.progressed( ByteBuffer.wrap( buffer, 0, bytes ) );
141                 }
142                 catch ( Exception e )
143                 {
144                     // too bad
145                 }
146             }
147         }
148 
149         return total;
150     }
151 
152     public void move( File source, File target )
153             throws IOException
154     {
155         if ( !source.renameTo( target ) )
156         {
157             copy( source, target );
158 
159             target.setLastModified( source.lastModified() );
160 
161             source.delete();
162         }
163     }
164 
165     @Override
166     public String readChecksum( final File checksumFile ) throws IOException
167     {
168         // for now do exactly same as happened before, but FileProcessor is a component and can be replaced
169         return ChecksumUtils.read( checksumFile );
170     }
171 
172     @Override
173     public void writeChecksum( final File checksumFile, final String checksum ) throws IOException
174     {
175         // for now do exactly same as happened before, but FileProcessor is a component and can be replaced
176         write( checksumFile, checksum );
177     }
178 }