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 binaryParser;
49      
50      private SourceModuleInfoParser sourceParser;
51  
52      public LocationManager()
53      {
54          this.binaryParser = new BinaryModuleInfoParser();
55          this.sourceParser = new SourceModuleInfoParser();
56      }
57      
58      LocationManager( ModuleInfoParser binaryParser, SourceModuleInfoParser sourceParser )
59      {
60          this.binaryParser = binaryParser;
61          this.sourceParser = sourceParser;
62      }
63  
64      /**
65       * Decide for every {@code request.getPathElements()} if it belongs to the modulePath or classPath, based on the
66       * {@code request.getMainModuleDescriptor()}.
67       * 
68       * @param request the paths to resolve
69       * @return the result of the resolution
70       * @throws IOException if a critical IOException occurs
71       */
72      public <T> ResolvePathsResult<T> resolvePaths( ResolvePathsRequest<T> request )
73          throws IOException
74      {
75          ResolvePathsResult<T> result = request.createResult();
76          
77          Map<T, JavaModuleDescriptor> pathElements = new LinkedHashMap<>( request.getPathElements().size() );
78  
79          JavaModuleDescriptor mainModuleDescriptor;
80          
81          Path descriptorPath = request.getMainModuleDescriptor();
82          
83          if ( descriptorPath != null )
84          {
85              if ( descriptorPath.endsWith( "module-info.java" ) )
86              {
87                  mainModuleDescriptor = sourceParser.fromSourcePath( descriptorPath );
88              }
89              else if ( descriptorPath.endsWith( "module-info.class" ) )
90              {
91                  mainModuleDescriptor = binaryParser.getModuleDescriptor( descriptorPath.getParent() );
92              }
93              else
94              {
95                  throw new IOException( "Invalid path to module descriptor: " + descriptorPath );
96              }
97          }
98          else
99          {
100             mainModuleDescriptor = null;
101         }
102 
103         Map<String, JavaModuleDescriptor> availableNamedModules = new HashMap<>(); 
104         
105         Map<String, ModuleNameSource> moduleNameSources = new HashMap<>();
106         
107         // start from root
108         result.setMainModuleDescriptor( mainModuleDescriptor );
109         
110         Map<T, Path> filenameAutoModules = new HashMap<>();
111         
112         ManifestModuleNameExtractor manifestModuleNameExtractor = new ManifestModuleNameExtractor();
113 
114         // collect all modules from path
115         for ( T t : request.getPathElements() )
116         {
117             Path path = request.toPath( t );
118             
119             JavaModuleDescriptor moduleDescriptor = null;
120             ModuleNameSource source = null;
121             
122             // either jar or outputDirectory
123             if ( Files.isRegularFile( path ) || Files.exists( path.resolve( "module-info.class" ) ) )
124             {
125                 try
126                 {
127                     moduleDescriptor = binaryParser.getModuleDescriptor( path );
128                 }
129                 catch( IOException e )
130                 {
131                     result.getPathExceptions().put( t, e );
132                     continue;
133                 }
134             }
135 
136             if ( moduleDescriptor != null ) 
137             {
138                 source = ModuleNameSource.MODULEDESCRIPTOR;
139             }
140             else
141             {
142                 String moduleName = manifestModuleNameExtractor.extract( path );
143 
144                 if ( moduleName != null )
145                 {
146                     source = ModuleNameSource.MANIFEST;
147                 }
148                 else if ( request.getJdkHome() != null )
149                 {
150                     // Will require external JVM, which is considered slow(er)
151                     // Collect first, next resolve all at once
152                     filenameAutoModules.put( t, path );
153                 }
154                 else 
155                 {
156                     try
157                     {
158                         moduleName = MainClassModuleNameExtractor.getModuleName( path );
159                     }
160                     catch ( Exception e )
161                     {
162                         result.getPathExceptions().put( t, e );
163                         continue;
164                     }
165                     
166                     if ( moduleName != null )
167                     {
168                         source = ModuleNameSource.FILENAME;
169                     }
170                 }
171 
172                 if ( moduleName != null )
173                 {
174                     moduleDescriptor = JavaModuleDescriptor.newAutomaticModule( moduleName ).build();
175                 }
176             }
177             
178             if ( moduleDescriptor != null )
179             {
180                 moduleNameSources.put( moduleDescriptor.name(), source );
181                 
182                 availableNamedModules.put( moduleDescriptor.name(), moduleDescriptor );
183             }
184             
185             pathElements.put( t, moduleDescriptor );
186             
187         }
188         result.setPathElements( pathElements );
189         
190         if ( !filenameAutoModules.isEmpty() ) 
191         {
192             MainClassModuleNameExtractor extractor = new MainClassModuleNameExtractor( request.getJdkHome() );
193             
194             Map<T, String> automodules = extractor.extract( filenameAutoModules );
195             
196             for ( Map.Entry<T, String> entry : automodules.entrySet() )
197             {
198                 String moduleName = entry.getValue();
199                 
200                 if ( moduleName != null )
201                 {
202                     JavaModuleDescriptor moduleDescriptor = JavaModuleDescriptor.newAutomaticModule( moduleName ).build();
203                     
204                     moduleNameSources.put( moduleDescriptor.name(), ModuleNameSource.FILENAME );
205                     
206                     availableNamedModules.put( moduleDescriptor.name(), moduleDescriptor );
207                     
208                     pathElements.put( entry.getKey(), moduleDescriptor );
209                 }
210             }
211         }
212         
213         if ( mainModuleDescriptor != null )
214         {
215             Set<String> requiredNamedModules = new HashSet<>();
216             
217             requiredNamedModules.add( mainModuleDescriptor.name() );
218             
219             requiredNamedModules.addAll( request.getAdditionalModules() );
220             
221             select( mainModuleDescriptor, Collections.unmodifiableMap( availableNamedModules ), requiredNamedModules );
222 
223             // in case of identical module names, first one wins
224             Set<String> collectedModules = new HashSet<>( requiredNamedModules.size() );
225 
226             for ( Entry<T, JavaModuleDescriptor> entry : pathElements.entrySet() )
227             {
228                 if ( entry.getValue() != null && requiredNamedModules.contains( entry.getValue().name() ) )
229                 {
230                     if ( collectedModules.add( entry.getValue().name() ) )
231                     {
232                         result.getModulepathElements().put( entry.getKey(),
233                                                             moduleNameSources.get( entry.getValue().name() ) );
234                     }
235                 }
236                 else
237                 {
238                     result.getClasspathElements().add( entry.getKey() );
239                 }
240             }
241         }
242 
243         return result;
244     }
245 
246     private void select( JavaModuleDescriptor module, Map<String, JavaModuleDescriptor> availableModules,
247                          Set<String> namedModules )
248     {
249         for ( JavaModuleDescriptor.JavaRequires requires : module.requires() )
250         {
251             String requiresName = requires.name();
252             JavaModuleDescriptor requiredModule = availableModules.get( requiresName );
253 
254             if ( requiredModule != null && namedModules.add( requiresName ) )
255             {
256                 select( requiredModule, availableModules, namedModules );
257             }
258         }
259     }
260 }