View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl;
20  
21  import javax.inject.Named;
22  import javax.inject.Singleton;
23  
24  import java.io.BufferedInputStream;
25  import java.io.BufferedOutputStream;
26  import java.io.File;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.OutputStream;
30  import java.io.UncheckedIOException;
31  import java.nio.ByteBuffer;
32  import java.nio.charset.StandardCharsets;
33  import java.nio.file.Files;
34  import java.nio.file.StandardCopyOption;
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 implements FileProcessor {
46  
47      /**
48       * Thread-safe variant of {@link File#mkdirs()}. Creates the directory named by the given abstract pathname,
49       * including any necessary but nonexistent parent directories. Note that if this operation fails it may have
50       * succeeded in creating some of the necessary parent directories.
51       *
52       * @param directory The directory to create, may be {@code null}.
53       * @return {@code true} if and only if the directory was created, along with all necessary parent directories;
54       * {@code false} otherwise
55       */
56      public boolean mkdirs(File directory) {
57          if (directory == null) {
58              return false;
59          }
60  
61          if (directory.exists()) {
62              return false;
63          }
64          if (directory.mkdir()) {
65              return true;
66          }
67  
68          File canonDir;
69          try {
70              canonDir = directory.getCanonicalFile();
71          } catch (IOException e) {
72              throw new UncheckedIOException(e);
73          }
74  
75          File parentDir = canonDir.getParentFile();
76          return (parentDir != null && (mkdirs(parentDir) || parentDir.exists()) && canonDir.mkdir());
77      }
78  
79      public void write(File target, String data) throws IOException {
80          FileUtils.writeFile(target.toPath(), p -> Files.write(p, data.getBytes(StandardCharsets.UTF_8)));
81      }
82  
83      public void write(File target, InputStream source) throws IOException {
84          FileUtils.writeFile(target.toPath(), p -> Files.copy(source, p, StandardCopyOption.REPLACE_EXISTING));
85      }
86  
87      public void copy(File source, File target) throws IOException {
88          copy(source, target, null);
89      }
90  
91      public long copy(File source, File target, ProgressListener listener) throws IOException {
92          try (InputStream in = new BufferedInputStream(Files.newInputStream(source.toPath()));
93                  FileUtils.CollocatedTempFile tempTarget = FileUtils.newTempFile(target.toPath());
94                  OutputStream out = new BufferedOutputStream(Files.newOutputStream(tempTarget.getPath()))) {
95              long result = copy(out, in, listener);
96              tempTarget.move();
97              return result;
98          }
99      }
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 }