src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/IOUtils.java
branchJDK-8200758-branch
changeset 58994 b09ba68c6a19
parent 58762 0fe62353385b
equal deleted inserted replaced
58993:b5e1baa9d2c3 58994:b09ba68c6a19
       
     1 /*
       
     2  * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package jdk.incubator.jpackage.internal;
       
    27 
       
    28 import java.io.*;
       
    29 import java.lang.reflect.InvocationHandler;
       
    30 import java.lang.reflect.Method;
       
    31 import java.lang.reflect.Proxy;
       
    32 import java.nio.channels.FileChannel;
       
    33 import java.nio.file.FileVisitResult;
       
    34 import java.nio.file.Files;
       
    35 import java.nio.file.Path;
       
    36 import java.nio.file.SimpleFileVisitor;
       
    37 import java.nio.file.attribute.BasicFileAttributes;
       
    38 import java.util.*;
       
    39 import javax.xml.stream.XMLOutputFactory;
       
    40 import javax.xml.stream.XMLStreamException;
       
    41 import javax.xml.stream.XMLStreamWriter;
       
    42 
       
    43 /**
       
    44  * IOUtils
       
    45  *
       
    46  * A collection of static utility methods.
       
    47  */
       
    48 public class IOUtils {
       
    49 
       
    50     public static void deleteRecursive(File path) throws IOException {
       
    51         if (!path.exists()) {
       
    52             return;
       
    53         }
       
    54         Path directory = path.toPath();
       
    55         Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
       
    56             @Override
       
    57             public FileVisitResult visitFile(Path file,
       
    58                             BasicFileAttributes attr) throws IOException {
       
    59                 if (Platform.getPlatform() == Platform.WINDOWS) {
       
    60                     Files.setAttribute(file, "dos:readonly", false);
       
    61                 }
       
    62                 Files.delete(file);
       
    63                 return FileVisitResult.CONTINUE;
       
    64             }
       
    65 
       
    66             @Override
       
    67             public FileVisitResult preVisitDirectory(Path dir,
       
    68                             BasicFileAttributes attr) throws IOException {
       
    69                 if (Platform.getPlatform() == Platform.WINDOWS) {
       
    70                     Files.setAttribute(dir, "dos:readonly", false);
       
    71                 }
       
    72                 return FileVisitResult.CONTINUE;
       
    73             }
       
    74 
       
    75             @Override
       
    76             public FileVisitResult postVisitDirectory(Path dir, IOException e)
       
    77                             throws IOException {
       
    78                 Files.delete(dir);
       
    79                 return FileVisitResult.CONTINUE;
       
    80             }
       
    81         });
       
    82     }
       
    83 
       
    84     public static void copyRecursive(Path src, Path dest) throws IOException {
       
    85         copyRecursive(src, dest, List.of());
       
    86     }
       
    87 
       
    88     public static void copyRecursive(Path src, Path dest,
       
    89             final List<String> excludes) throws IOException {
       
    90         Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
       
    91             @Override
       
    92             public FileVisitResult preVisitDirectory(final Path dir,
       
    93                     final BasicFileAttributes attrs) throws IOException {
       
    94                 if (excludes.contains(dir.toFile().getName())) {
       
    95                     return FileVisitResult.SKIP_SUBTREE;
       
    96                 } else {
       
    97                     Files.createDirectories(dest.resolve(src.relativize(dir)));
       
    98                     return FileVisitResult.CONTINUE;
       
    99                 }
       
   100             }
       
   101 
       
   102             @Override
       
   103             public FileVisitResult visitFile(final Path file,
       
   104                     final BasicFileAttributes attrs) throws IOException {
       
   105                 if (!excludes.contains(file.toFile().getName())) {
       
   106                     Files.copy(file, dest.resolve(src.relativize(file)));
       
   107                 }
       
   108                 return FileVisitResult.CONTINUE;
       
   109             }
       
   110         });
       
   111     }
       
   112 
       
   113     public static void copyFile(File sourceFile, File destFile)
       
   114             throws IOException {
       
   115         destFile.getParentFile().mkdirs();
       
   116 
       
   117         //recreate the file as existing copy may have weird permissions
       
   118         destFile.delete();
       
   119         destFile.createNewFile();
       
   120 
       
   121         try (FileChannel source = new FileInputStream(sourceFile).getChannel();
       
   122             FileChannel destination =
       
   123                     new FileOutputStream(destFile).getChannel()) {
       
   124 
       
   125             if (destination != null && source != null) {
       
   126                 destination.transferFrom(source, 0, source.size());
       
   127             }
       
   128         }
       
   129 
       
   130         //preserve executable bit!
       
   131         if (sourceFile.canExecute()) {
       
   132             destFile.setExecutable(true, false);
       
   133         }
       
   134         if (!sourceFile.canWrite()) {
       
   135             destFile.setReadOnly();
       
   136         }
       
   137         destFile.setReadable(true, false);
       
   138     }
       
   139 
       
   140     // run "launcher paramfile" in the directory where paramfile is kept
       
   141     public static void run(String launcher, File paramFile)
       
   142             throws IOException {
       
   143         if (paramFile != null && paramFile.exists()) {
       
   144             ProcessBuilder pb =
       
   145                     new ProcessBuilder(launcher, paramFile.getName());
       
   146             pb = pb.directory(paramFile.getParentFile());
       
   147             exec(pb);
       
   148         }
       
   149     }
       
   150 
       
   151     public static void exec(ProcessBuilder pb)
       
   152             throws IOException {
       
   153         exec(pb, false, null);
       
   154     }
       
   155 
       
   156     static void exec(ProcessBuilder pb, boolean testForPresenseOnly,
       
   157             PrintStream consumer) throws IOException {
       
   158         List<String> output = new ArrayList<>();
       
   159         Executor exec = Executor.of(pb).setOutputConsumer(lines -> {
       
   160             lines.forEach(output::add);
       
   161             if (consumer != null) {
       
   162                 output.forEach(consumer::println);
       
   163             }
       
   164         });
       
   165 
       
   166         if (testForPresenseOnly) {
       
   167             exec.execute();
       
   168         } else {
       
   169             exec.executeExpectSuccess();
       
   170         }
       
   171     }
       
   172 
       
   173     public static int getProcessOutput(List<String> result, String... args)
       
   174             throws IOException, InterruptedException {
       
   175 
       
   176         ProcessBuilder pb = new ProcessBuilder(args);
       
   177 
       
   178         final Process p = pb.start();
       
   179 
       
   180         List<String> list = new ArrayList<>();
       
   181 
       
   182         final BufferedReader in =
       
   183                 new BufferedReader(new InputStreamReader(p.getInputStream()));
       
   184         final BufferedReader err =
       
   185                 new BufferedReader(new InputStreamReader(p.getErrorStream()));
       
   186 
       
   187         Thread t = new Thread(() -> {
       
   188             try {
       
   189                 String line;
       
   190                 while ((line = in.readLine()) != null) {
       
   191                     list.add(line);
       
   192                 }
       
   193             } catch (IOException ioe) {
       
   194                 Log.verbose(ioe);
       
   195             }
       
   196 
       
   197             try {
       
   198                 String line;
       
   199                 while ((line = err.readLine()) != null) {
       
   200                     Log.error(line);
       
   201                 }
       
   202             } catch (IOException ioe) {
       
   203                   Log.verbose(ioe);
       
   204             }
       
   205         });
       
   206         t.setDaemon(true);
       
   207         t.start();
       
   208 
       
   209         int ret = p.waitFor();
       
   210 
       
   211         result.clear();
       
   212         result.addAll(list);
       
   213 
       
   214         return ret;
       
   215     }
       
   216 
       
   217     static void writableOutputDir(Path outdir) throws PackagerException {
       
   218         File file = outdir.toFile();
       
   219 
       
   220         if (!file.isDirectory() && !file.mkdirs()) {
       
   221             throw new PackagerException("error.cannot-create-output-dir",
       
   222                     file.getAbsolutePath());
       
   223         }
       
   224         if (!file.canWrite()) {
       
   225             throw new PackagerException("error.cannot-write-to-output-dir",
       
   226                     file.getAbsolutePath());
       
   227         }
       
   228     }
       
   229 
       
   230     public static Path replaceSuffix(Path path, String suffix) {
       
   231         Path parent = path.getParent();
       
   232         String filename = path.getFileName().toString().replaceAll("\\.[^.]*$", "")
       
   233                 + Optional.ofNullable(suffix).orElse("");
       
   234         return parent != null ? parent.resolve(filename) : Path.of(filename);
       
   235     }
       
   236 
       
   237     public static Path addSuffix(Path path, String suffix) {
       
   238         Path parent = path.getParent();
       
   239         String filename = path.getFileName().toString() + suffix;
       
   240         return parent != null ? parent.resolve(filename) : Path.of(filename);
       
   241     }
       
   242 
       
   243     public static String getSuffix(Path path) {
       
   244         String filename = replaceSuffix(path.getFileName(), null).toString();
       
   245         return path.getFileName().toString().substring(filename.length());
       
   246     }
       
   247 
       
   248     @FunctionalInterface
       
   249     public static interface XmlConsumer {
       
   250         void accept(XMLStreamWriter xml) throws IOException, XMLStreamException;
       
   251     }
       
   252 
       
   253     public static void createXml(Path dstFile, XmlConsumer xmlConsumer) throws
       
   254             IOException {
       
   255         XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
       
   256         try (Writer w = Files.newBufferedWriter(dstFile)) {
       
   257             // Wrap with pretty print proxy
       
   258             XMLStreamWriter xml = (XMLStreamWriter) Proxy.newProxyInstance(
       
   259                     XMLStreamWriter.class.getClassLoader(), new Class<?>[]{
       
   260                 XMLStreamWriter.class}, new PrettyPrintHandler(
       
   261                     xmlFactory.createXMLStreamWriter(w)));
       
   262 
       
   263             xml.writeStartDocument();
       
   264             xmlConsumer.accept(xml);
       
   265             xml.writeEndDocument();
       
   266             xml.flush();
       
   267             xml.close();
       
   268         } catch (XMLStreamException ex) {
       
   269             throw new IOException(ex);
       
   270         } catch (IOException ex) {
       
   271             throw ex;
       
   272         }
       
   273     }
       
   274 
       
   275     private static class PrettyPrintHandler implements InvocationHandler {
       
   276 
       
   277         PrettyPrintHandler(XMLStreamWriter target) {
       
   278             this.target = target;
       
   279         }
       
   280 
       
   281         @Override
       
   282         public Object invoke(Object proxy, Method method, Object[] args) throws
       
   283                 Throwable {
       
   284             switch (method.getName()) {
       
   285                 case "writeStartElement":
       
   286                     // update state of parent node
       
   287                     if (depth > 0) {
       
   288                         hasChildElement.put(depth - 1, true);
       
   289                     }
       
   290                     // reset state of current node
       
   291                     hasChildElement.put(depth, false);
       
   292                     // indent for current depth
       
   293                     target.writeCharacters(EOL);
       
   294                     target.writeCharacters(repeat(depth, INDENT));
       
   295                     depth++;
       
   296                     break;
       
   297                 case "writeEndElement":
       
   298                     depth--;
       
   299                     if (hasChildElement.get(depth) == true) {
       
   300                         target.writeCharacters(EOL);
       
   301                         target.writeCharacters(repeat(depth, INDENT));
       
   302                     }
       
   303                     break;
       
   304                 case "writeProcessingInstruction":
       
   305                 case "writeEmptyElement":
       
   306                     // update state of parent node
       
   307                     if (depth > 0) {
       
   308                         hasChildElement.put(depth - 1, true);
       
   309                     }
       
   310                     // indent for current depth
       
   311                     target.writeCharacters(EOL);
       
   312                     target.writeCharacters(repeat(depth, INDENT));
       
   313                     break;
       
   314                 default:
       
   315                     break;
       
   316             }
       
   317             method.invoke(target, args);
       
   318             return null;
       
   319         }
       
   320 
       
   321         private static String repeat(int d, String s) {
       
   322             StringBuilder sb = new StringBuilder();
       
   323             while (d-- > 0) {
       
   324                 sb.append(s);
       
   325             }
       
   326             return sb.toString();
       
   327         }
       
   328 
       
   329         private final XMLStreamWriter target;
       
   330         private int depth = 0;
       
   331         private final Map<Integer, Boolean> hasChildElement = new HashMap<>();
       
   332         private static final String INDENT = "  ";
       
   333         private static final String EOL = "\n";
       
   334     }
       
   335 }