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