View Javadoc

1   /*
2    * Copyright 2006-2012 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.maven.util.eclipse;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.util.Arrays;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Set;
25  
26  import org.apache.maven.artifact.repository.ArtifactRepository;
27  import org.apache.maven.plugin.logging.Log;
28  import org.apache.maven.profiles.ProfileManager;
29  import org.apache.maven.project.MavenProject;
30  import org.apache.maven.project.MavenProjectBuilder;
31  import org.codehaus.plexus.util.DirectoryScanner;
32  
33  import de.smartics.maven.util.project.MavenContext;
34  
35  /**
36   * This utility class provides functions that deal with the eclipse workspace
37   * useful for Maven plugins.
38   * 
39   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
40   * @version $Revision: 12040 $
41   */
42  public final class WorkspaceUtils
43  {
44    // ********************************* Fields *********************************
45  
46    // --- constants ------------------------------------------------------------
47  
48    /**
49     * The default name of the POM file.
50     * <p>
51     * The value of this constant is {@value}.
52     */
53    private static final String DEFAULT_POM_FILENAME = "pom.xml";
54  
55    // --- members --------------------------------------------------------------
56  
57    // ****************************** Initializer *******************************
58  
59    // ****************************** Constructors ******************************
60  
61    /**
62     * Utiltiy class pattern.
63     */
64    private WorkspaceUtils()
65    {
66    }
67  
68    // ****************************** Inner Classes *****************************
69  
70    // ********************************* Methods ********************************
71  
72    // --- init -----------------------------------------------------------------
73  
74    // --- get&set --------------------------------------------------------------
75  
76    // --- business -------------------------------------------------------------
77  
78    /**
79     * Returns the set of Maven projects found in the given workspace directory.
80     * <p>
81     * This method looks for POM files in the root directory (named
82     * <code>pom.xml</code>). If it finds one this will be read and added to the
83     * set if it provides a group and artifact identifier.
84     * <p>
85     * If it references submodules this references will not be followed.
86     * Submodules may, however, be found by other means, that is they are not
87     * especially excluded.
88     * 
89     * @param context provides information required by this service.
90     * @param workspaceDir the workspace directory to search POM files.
91     * @param includes the set of directories (not <code>pom.xml</code> files) as
92     *          {@link String}s to include in the search. May be and should be
93     *          <code>null</code> in which case only the direct contents of the
94     *          workspace directories are scanned.
95     * @param excludes the set of excludes as {@link String}s to exclude from
96     *          search. May be <code>null</code> in which case nothing is
97     *          excluded.
98     * @return the set of {@link MavenProject} projects found.
99     */
100   public static Set<MavenProject> fetchMavenProjects(
101       final MavenContext context,
102       final File workspaceDir,
103       final Set<String> includes,
104       final Set<String> excludes)
105   {
106     return fetchMavenProjects(context, workspaceDir, includes, excludes, false);
107   }
108 
109   /**
110    * Returns the set of Maven projects found in the given workspace directory.
111    * <p>
112    * This method looks for POM files in the root directory (named
113    * <code>pom.xml</code>). If it finds one this will be read and added to the
114    * set if it provides a group and artifact identifier.
115    * <p>
116    * If it references submodules this references will not be followed.
117    * 
118    * @param context provides information required by this service.
119    * @param workspaceDir the workspace directory to search POM files.
120    * @param includes the set of directories (not <code>pom.xml</code> files) as
121    *          {@link String}s to include in the search. May be and should be
122    *          <code>null</code> in which case only the direct contents of the
123    *          workspace directories are scanned.
124    * @param excludes the set of excludes as {@link String}s to exclude from
125    *          search. May be <code>null</code> in which case nothing is
126    *          excluded.
127    * @param filterSubprojects if this flag is set to <code>true</code> sub
128    *          projects (referenced as modules from a project) are removed from
129    *          the list of projects, otherwise the returned set of projects is
130    *          not filtered.
131    * @return the set of {@link MavenProject} projects found.
132    */
133   public static Set<MavenProject> fetchMavenProjects(
134       final MavenContext context,
135       final File workspaceDir,
136       final Set<String> includes,
137       final Set<String> excludes,
138       final boolean filterSubprojects)
139   {
140     final Log log = context.getLog();
141     final Set<MavenProject> projects = new HashSet<MavenProject>();
142 
143     final DirectoryScanner scanner =
144         createScanner(workspaceDir, includes, excludes);
145     final String[] dirs = scanner.getIncludedDirectories();
146     final MavenProjectBuilder projectBuilder = context.getProjectBuilder();
147     final ArtifactRepository artifactRepository =
148         context.getLocalArtifactRepository();
149     final ProfileManager profileManager = context.getProfileManager();
150     final Set<String> modules = new HashSet<String>();
151     for (String dirName : Arrays.asList(dirs))
152     {
153       final File dir = new File(workspaceDir, dirName);
154       if (dir.exists())
155       {
156         final File pomFile = new File(dir, DEFAULT_POM_FILENAME);
157         if (pomFile.exists())
158         {
159           try
160           {
161             final MavenProject project =
162                 projectBuilder.build(pomFile, artifactRepository,
163                     profileManager, false);
164             if (filterSubprojects)
165             {
166               addModules(log, modules, project);
167             }
168             final boolean isAdded = projects.add(project);
169             if (!isAdded)
170             {
171               if (log.isDebugEnabled())
172               {
173                 final String projectId =
174                     project.getGroupId() + ':' + project.getArtifactId();
175                 final String message =
176                     Messages.getString("project.skipped", projectId, pomFile);
177                 log.debug(message);
178               }
179             }
180             else
181             {
182               if (log.isDebugEnabled())
183               {
184                 final String projectId =
185                     project.getGroupId() + ':' + project.getArtifactId();
186 
187                 final String message =
188                     Messages.getString(filterSubprojects
189                         ? "project.addedTemporary" : "project.added",
190                         projectId, pomFile);
191                 log.debug(message);
192               }
193             }
194           }
195           catch (final Exception e)
196           {
197             if (log.isInfoEnabled())
198             {
199               final String message =
200                   Messages.getString("pomFile.unreadable", pomFile);
201               if (log.isDebugEnabled())
202               {
203                 log.debug(message, e);
204               }
205               else
206               {
207                 log.info(message);
208               }
209             }
210           }
211         }
212       }
213     }
214 
215     if (filterSubprojects)
216     {
217       filterSubprojects(log, modules, projects);
218     }
219 
220     return projects;
221   }
222 
223   /**
224    * Returns the set of IDs of Maven projects found in the given workspace
225    * directory.
226    * <p>
227    * This method looks for POM files in the root directory (named
228    * <code>pom.xml</code>). If it finds one this will be read and added to the
229    * set if it provides a group and artifact identifier.
230    * <p>
231    * If it references submodules this references will not be followed.
232    * 
233    * @param context provides information required by this service.
234    * @param workspaceDir the workspace directory to search POM files.
235    * @param includes the set of directories (not <code>pom.xml</code> files) as
236    *          {@link String}s to include in the search. May be and should be
237    *          <code>null</code> in which case only the direct contents of the
238    *          workspace directories are scanned.
239    * @param excludes the set of excludes as {@link String}s to exclude from
240    *          search. May be <code>null</code> in which case nothing is
241    *          excluded.
242    * @param filterSubprojects if this flag is set to <code>true</code> sub
243    *          projects (referenced as modules from a project) are removed from
244    *          the list of projects, otherwise the returned set of projects is
245    *          not filtered.
246    * @return the set of project IDs ({@link String}s) found.
247    */
248   public static Set<String> fetchMavenProjectIds(
249       final MavenContext context,
250       final File workspaceDir,
251       final Set<String> includes,
252       final Set<String> excludes,
253       final boolean filterSubprojects)
254   {
255     final Set<MavenProject> projects =
256         fetchMavenProjects(context, workspaceDir, includes, excludes,
257             filterSubprojects);
258     final Set<String> ids = new HashSet<String>(projects.size());
259     for (MavenProject project : projects)
260     {
261       final String id = project.getGroupId() + ':' + project.getArtifactId();
262       ids.add(id);
263     }
264 
265     return ids;
266   }
267 
268   // ... helper ...............................................................
269 
270   /**
271    * Removes all subprojects from the project set.
272    * 
273    * @param log the plugin logger to use.
274    * @param modules the references to the directories ({@link String}s) of
275    *          subprojects.
276    * @param projects the {@link MavenProject} projects to filter.
277    */
278   private static void filterSubprojects(
279       final Log log,
280       final Set<String> modules,
281       final Set<MavenProject> projects)
282   {
283     for (final Iterator<MavenProject> i = projects.iterator(); i.hasNext();)
284     {
285       final MavenProject project = i.next();
286       final File dir = project.getBasedir();
287       try
288       {
289         final String path = dir.getCanonicalPath();
290         if (modules.contains(path))
291         {
292           i.remove();
293           if (log.isDebugEnabled())
294           {
295             final String id =
296                 project.getGroupId() + ':' + project.getArtifactId();
297             final String message =
298                 Messages.getString("project.removed", id, path);
299             log.debug(message);
300           }
301         }
302       }
303       catch (final IOException e)
304       {
305         // ignore this and do not remove...
306         if (log.isWarnEnabled())
307         {
308           final String message =
309               Messages
310                   .getString("project.filterFailure", dir.getAbsolutePath());
311           if (log.isDebugEnabled())
312           {
313             log.debug(message, e);
314           }
315           else
316           {
317             log.warn(message);
318           }
319         }
320       }
321     }
322   }
323 
324   /**
325    * Adds the base directories of referenced subprojects (modules) to the list
326    * of sub projects.
327    * 
328    * @param log the plugin logger to use.
329    * @param modules the set of subprojects ({@link String}a) to add to.
330    * @param project the project to check for subporjects (modules);
331    */
332   @SuppressWarnings("unchecked")
333   private static void addModules(
334       final Log log,
335       final Set<String> modules,
336       final MavenProject project)
337   {
338     final List<String> modulePathes = project.getModules();
339 
340     final File baseDir = project.getBasedir();
341     for (String path : modulePathes)
342     {
343       File file = new File(baseDir, path);
344       try
345       {
346         if (file.exists())
347         {
348           modules.add(file.getCanonicalPath());
349           if (log.isDebugEnabled())
350           {
351             final String id =
352                 project.getGroupId() + ':' + project.getArtifactId();
353             final String message =
354                 Messages.getString("project.submoduleFound", id, file);
355             log.debug(message);
356           }
357         }
358         else
359         {
360           file = new File(path);
361           if (file.exists())
362           {
363             modules.add(file.getCanonicalPath());
364             if (log.isDebugEnabled())
365             {
366               final String id =
367                   project.getGroupId() + ':' + project.getArtifactId();
368               final String message =
369                   Messages.getString("project.submoduleFound", id, file);
370               log.debug(message);
371             }
372           }
373         }
374       }
375       catch (final IOException e)
376       {
377         // ignore this...
378         if (log.isWarnEnabled())
379         {
380           final String message =
381               Messages.getString("project.moduleAddFailure", file);
382           if (log.isDebugEnabled())
383           {
384             log.debug(message, e);
385           }
386           else
387           {
388             log.warn(message);
389           }
390         }
391       }
392     }
393 
394   }
395 
396   /**
397    * Helper to create the scanner with the calculated includes and excludes.
398    * 
399    * @param rootDir the root directory to use for includes/excludes.
400    * @param includes the set of includes ({@link String}s) to include in the
401    *          search. If this is <code>null</code> or empty, we will only add
402    *          the immediate children of the root directory.
403    * @param excludes the set of excludes ({@link String}s) to exclude from
404    *          search.
405    * @return the scanner with the calculated includes and excludes.
406    */
407   private static DirectoryScanner createScanner(
408       final File rootDir,
409       final Set<String> includes,
410       final Set<String> excludes)
411   {
412     final DirectoryScanner scanner = new DirectoryScanner();
413     scanner.setBasedir(rootDir);
414     scanner.setCaseSensitive(true);
415     final String[] includesArray;
416     if (includes == null || includes.isEmpty())
417     {
418       includesArray = new String[]
419       { "*" };
420     }
421     else
422     {
423       includesArray = (String[]) includes.toArray(new String[includes.size()]);
424     }
425     scanner.setIncludes(includesArray);
426 
427     if (excludes != null)
428     {
429       final String[] excludesArray =
430           (String[]) excludes.toArray(new String[excludes.size()]);
431       scanner.setExcludes(excludesArray);
432     }
433 
434     scanner.scan();
435 
436     return scanner;
437   }
438 
439   // --- object basics --------------------------------------------------------
440 
441 }