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.IOException;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.LinkedHashMap;
29  import java.util.Map;
30  import java.util.Map.Entry;
31  import java.util.Set;
32  
33  import javax.inject.Singleton;
34  
35  import org.codehaus.plexus.component.annotations.Component;
36  import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult.ModuleNameSource;
37  
38  /**
39   * Maps artifacts to modules and analyzes the type of required modules
40   * 
41   * @author Robert Scholte
42   * @since 1.0.0
43   */
44  @Singleton
45  @Component( role = LocationManager.class )
46  public class LocationManager
47  {
48      private ModuleInfoParser asmParser;
49      
50      private QDoxModuleInfoParser qdoxParser;
51  
52      public LocationManager()
53      {
54          this.asmParser = new AsmModuleInfoParser();
55          this.qdoxParser = new QDoxModuleInfoParser();
56      }
57      
58      LocationManager( ModuleInfoParser asmParser, QDoxModuleInfoParser qdoxParser )
59      {
60          this.asmParser = asmParser;
61          this.qdoxParser = qdoxParser;
62      }
63  
64      public <T> ResolvePathsResult<T> resolvePaths( ResolvePathsRequest<T> request )
65          throws IOException
66      {
67          ResolvePathsResult<T> result = request.createResult();
68          
69          Map<T, JavaModuleDescriptor> pathElements = new LinkedHashMap<>( request.getPathElements().size() );
70  
71          JavaModuleDescriptor mainModuleDescriptor;
72          
73          Path descriptorPath = request.getMainModuleDescriptor();
74          
75          if ( descriptorPath != null )
76          {
77              if ( descriptorPath.endsWith( "module-info.java" ) )
78              {
79                  mainModuleDescriptor = qdoxParser.fromSourcePath( descriptorPath );
80              }
81              else if ( descriptorPath.endsWith( "module-info.class" ) )
82              {
83                  mainModuleDescriptor = asmParser.getModuleDescriptor( descriptorPath.getParent() );
84              }
85              else
86              {
87                  throw new IOException( "Invalid path to module descriptor: " + descriptorPath );
88              }
89          }
90          else
91          {
92              mainModuleDescriptor = null;
93          }
94  
95          Map<String, JavaModuleDescriptor> availableNamedModules = new HashMap<>(); 
96          
97          Map<String, ModuleNameSource> moduleNameSources = new HashMap<>();
98          
99          // start from root
100         result.setMainModuleDescriptor( mainModuleDescriptor );
101         
102         Map<T, Path> filenameAutoModules = new HashMap<>();
103         
104         ManifestModuleNameExtractor manifestModuleNameExtractor = new ManifestModuleNameExtractor();
105 
106         // collect all modules from path
107         for ( T t : request.getPathElements() )
108         {
109             Path path = request.toPath( t );
110             
111             JavaModuleDescriptor moduleDescriptor = null;
112             ModuleNameSource source = null;
113             
114             // either jar or outputDirectory
115             if ( Files.isRegularFile( path ) || Files.exists( path.resolve( "module-info.class" ) ) )
116             {
117                 moduleDescriptor = asmParser.getModuleDescriptor( path );
118             }
119 
120             if ( moduleDescriptor != null ) 
121             {
122                 source = ModuleNameSource.MODULEDESCRIPTOR;
123             }
124             else
125             {
126                 String moduleName = manifestModuleNameExtractor.extract( path );
127 
128                 if ( moduleName != null )
129                 {
130                     source = ModuleNameSource.MANIFEST;
131                 }
132                 else if ( request.getJdkHome() != null )
133                 {
134                     // Will require external JVM, which is considered slow(er)
135                     // Collect first, next resolve all at once
136                     filenameAutoModules.put( t, path );
137                 }
138                 else 
139                 {
140                     moduleName = MainClassModuleNameExtractor.getModuleName( path );
141                     
142                     if ( moduleName != null )
143                     {
144                         source = ModuleNameSource.FILENAME;
145                     }
146                 }
147 
148                 if ( moduleName != null )
149                 {
150                     moduleDescriptor = JavaModuleDescriptor.newAutomaticModule( moduleName ).build();
151                 }
152             }
153             
154             if ( moduleDescriptor != null )
155             {
156                 moduleNameSources.put( moduleDescriptor.name(), source );
157                 
158                 availableNamedModules.put( moduleDescriptor.name(), moduleDescriptor );
159             }
160             
161             pathElements.put( t, moduleDescriptor );
162             
163         }
164         result.setPathElements( pathElements );
165         
166         if ( !filenameAutoModules.isEmpty() ) 
167         {
168             MainClassModuleNameExtractor extractor = new MainClassModuleNameExtractor( request.getJdkHome() );
169             
170             Map<T, String> automodules = extractor.extract( filenameAutoModules );
171             
172             for ( Map.Entry<T, String> entry : automodules.entrySet() )
173             {
174                 String moduleName = entry.getValue();
175                 
176                 if ( moduleName != null )
177                 {
178                     JavaModuleDescriptor moduleDescriptor = JavaModuleDescriptor.newAutomaticModule( moduleName ).build();
179                     
180                     moduleNameSources.put( moduleDescriptor.name(), ModuleNameSource.FILENAME );
181                     
182                     availableNamedModules.put( moduleDescriptor.name(), moduleDescriptor );
183                     
184                     pathElements.put( entry.getKey(), moduleDescriptor );
185                 }
186             }
187         }
188         
189         if ( mainModuleDescriptor != null )
190         {
191             Set<String> requiredNamedModules = new HashSet<>();
192             
193             requiredNamedModules.add( mainModuleDescriptor.name() );
194             
195             select( mainModuleDescriptor, Collections.unmodifiableMap( availableNamedModules ), requiredNamedModules );
196 
197             for ( Entry<T, JavaModuleDescriptor> entry : pathElements.entrySet() )
198             {
199                 if ( entry.getValue() != null && requiredNamedModules.contains( entry.getValue().name() ) )
200                 {
201                     result.getModulepathElements().put( entry.getKey(), moduleNameSources.get( entry.getValue().name() ) );
202                 }
203                 else
204                 {
205                     result.getClasspathElements().add( entry.getKey() );
206                 }
207             }
208         }
209 
210         return result;
211     }
212 
213     private void select( JavaModuleDescriptor module, Map<String, JavaModuleDescriptor> availableModules,
214                          Set<String> namedModules )
215     {
216         for ( JavaModuleDescriptor.JavaRequires requires : module.requires() )
217         {
218             String requiresName = requires.name();
219             JavaModuleDescriptor requiredModule = availableModules.get( requiresName );
220 
221             if ( requiredModule != null && namedModules.add( requiresName ) )
222             {
223                 select( requiredModule, availableModules, namedModules );
224             }
225         }
226     }
227 }