View Javadoc
1   package org.codehaus.plexus.languages.java.jpms;
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 java.io.BufferedWriter;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.nio.charset.Charset;
26  import java.nio.file.FileVisitResult;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.nio.file.SimpleFileVisitor;
30  import java.nio.file.attribute.BasicFileAttributes;
31  import java.util.Collections;
32  import java.util.HashMap;
33  import java.util.Map;
34  import java.util.Properties;
35  
36  /**
37   * Extract the module name by calling the main method with an external JVM
38   *
39   * @author Robert Scholte
40   * @since 1.0.0
41   */
42  public class MainClassModuleNameExtractor {
43      private final Path jdkHome;
44  
45      public MainClassModuleNameExtractor(Path jdkHome) {
46          this.jdkHome = jdkHome;
47      }
48  
49      public <T> Map<T, String> extract(Map<T, Path> files) throws IOException {
50          Path workDir = Files.createTempDirectory("plexus-java_jpms-");
51  
52          String classResourcePath = CmdModuleNameExtractor.class.getName().replace('.', '/') + ".class";
53  
54          try (InputStream is =
55                  MainClassModuleNameExtractor.class.getResourceAsStream("/META-INF/versions/9/" + classResourcePath)) {
56              if (is == null) {
57                  return Collections.emptyMap();
58              }
59              Path target = workDir.resolve(classResourcePath);
60  
61              Files.createDirectories(target.getParent());
62  
63              Files.copy(is, target);
64          }
65  
66          try (BufferedWriter argsWriter = Files.newBufferedWriter(workDir.resolve("args"), Charset.defaultCharset())) {
67              argsWriter.append("--class-path");
68              argsWriter.newLine();
69  
70              argsWriter.append(".");
71              argsWriter.newLine();
72  
73              argsWriter.append(CmdModuleNameExtractor.class.getName());
74              argsWriter.newLine();
75  
76              for (Path p : files.values()) {
77                  // make sure the path is surrounded with quotes in case there is space
78                  argsWriter.append('"');
79                  // make sure to escape Windows paths
80                  argsWriter.append(p.toAbsolutePath().toString().replace("\\", "\\\\"));
81                  argsWriter.append('"');
82                  argsWriter.newLine();
83              }
84          }
85  
86          ProcessBuilder builder = new ProcessBuilder(
87                          jdkHome.resolve("bin/java").toAbsolutePath().toString(), "@args")
88                  .directory(workDir.toFile());
89  
90          Process p = builder.start();
91  
92          Properties output = new Properties();
93          try (InputStream is = p.getInputStream()) {
94              output.load(is);
95          }
96  
97          Map<T, String> moduleNames = new HashMap<>(files.size());
98          for (Map.Entry<T, Path> entry : files.entrySet()) {
99              moduleNames.put(
100                     entry.getKey(),
101                     output.getProperty(entry.getValue().toAbsolutePath().toString(), null));
102         }
103 
104         try {
105             Files.walkFileTree(workDir, new SimpleFileVisitor<Path>() {
106                 @Override
107                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
108                     Files.delete(file);
109                     return FileVisitResult.CONTINUE;
110                 }
111 
112                 @Override
113                 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
114                     Files.delete(dir);
115                     return FileVisitResult.CONTINUE;
116                 }
117             });
118         } catch (IOException e) {
119             // noop, we did our best to clean it up
120         }
121 
122         return moduleNames;
123     }
124 }