View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.shared.release.util;
20  
21  import java.io.File;
22  import java.io.IOException;
23  
24  import org.apache.maven.model.Model;
25  import org.apache.maven.model.Parent;
26  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
27  import org.codehaus.plexus.util.ReaderFactory;
28  import org.codehaus.plexus.util.xml.XmlStreamReader;
29  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
30  import org.slf4j.Logger;
31  
32  import static java.util.Objects.requireNonNull;
33  
34  /**
35   * <p>This utility class helps with finding a maven pom file
36   * which got parsed previously. It uses the fact that the
37   * combination of any parent ids plus the ids of the current
38   * pom itself is unique.</p>
39   * <p>This is e.g. needed for SCM systems which do not support
40   * sparse checkout but only can checkout the repository as whole
41   * like e.g. GIT. If the module which we are going to release is
42   * not in the parent directory, we first need to search for the
43   * 'right' sub directory in this case.
44   * subdirectory </p>
45   *
46   * Usage:
47   * <p>PomFinder is a statefull class. One instance of this class intended
48   * for a singular use! You need to create a new instance if you like
49   * to search for another pom.</p>
50   * <ol>
51   *   <li>
52   *     Parse an origin pom in a given directory with {@link #parsePom(java.io.File)}
53   *     This will act as the information about what to search for.
54   *   </li>
55   *   <li>
56   *      Search for the matching pom in a given tree using
57   *      {@link #findMatchingPom(java.io.File)}
58   *   </li>
59   * </ol>
60   *
61   * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
62   */
63  public class PomFinder {
64  
65      private final Logger log;
66      private PomInfo foundPomInfo;
67  
68      public PomFinder(Logger log) {
69          this.log = requireNonNull(log);
70      }
71  
72      /**
73       *
74       * @param originPom the pom File which should be used as blueprint for the search
75       * @return <code>true</code> if a pom got parsed successfully, <code>false</code> otherwise
76       */
77      public boolean parsePom(File originPom) {
78          if (!originPom.exists()) {
79              return false;
80          }
81  
82          try {
83              foundPomInfo = readPomInfo(originPom);
84          } catch (Exception e) {
85              log.warn("Error while parsing pom file", e);
86              return false;
87          }
88  
89          return foundPomInfo != null;
90      }
91  
92      /**
93       * Search for the previously with {@link #parsePom(java.io.File)}
94       * parsed pom in the given directory.
95       * @param startDirectory the initial directory
96       * @return the pom file which matches the previously parsed pom or <code>null</code>
97       * if no matching pom file could have been found.
98       */
99      public File findMatchingPom(File startDirectory) {
100         if (!startDirectory.exists()) {
101             return null;
102         }
103 
104         if (!startDirectory.isDirectory()) {
105             log.error("PomFinder must be started with a directory! Got " + startDirectory.getAbsolutePath());
106             return null;
107         }
108 
109         if (foundPomInfo == null) {
110             log.error("Please run parsePom first!");
111             return null;
112         }
113 
114         // look for the file in the current directory
115         File matchingPom = new File(startDirectory, foundPomInfo.getFileName());
116         if (matchingPom.exists()) {
117             PomInfo pi = null;
118             try {
119                 pi = readPomInfo(matchingPom);
120             } catch (Exception e) {
121                 log.warn("Error while parsing pom file", e);
122                 // do nothing, just continue with the search
123                 // this might happen if a build contains unfinished pom.xml
124                 // files in integration tests, etc
125             }
126 
127             if (pi == null || !pi.equals(foundPomInfo)) {
128                 matchingPom = null;
129             }
130         } else {
131             matchingPom = null;
132         }
133 
134         if (matchingPom == null) {
135             String[] childFiles = startDirectory.list();
136             if (childFiles != null) {
137                 for (String childFile : childFiles) {
138                     File subDir = new File(startDirectory, childFile);
139                     if (subDir.isDirectory() && !subDir.isHidden()) {
140                         matchingPom = findMatchingPom(subDir);
141                     }
142 
143                     if (matchingPom != null) {
144                         break;
145                     }
146                 }
147             }
148         }
149 
150         return matchingPom;
151     }
152 
153     /**
154      * Read the {@link PomInfo} from the given pom file
155      * @param pomFile pom.xml file
156      * @return the PomInfo or <code>null</code
157      */
158     private PomInfo readPomInfo(File pomFile) throws IOException, XmlPullParserException {
159         if (!pomFile.exists() || !pomFile.isFile()) {
160             return null;
161         }
162 
163         PomInfo pomInfo = null;
164 
165         MavenXpp3Reader reader = new MavenXpp3Reader();
166 
167         Model model;
168         try (XmlStreamReader xmlReader = ReaderFactory.newXmlReader(pomFile)) {
169             model = reader.read(xmlReader);
170         }
171 
172         if (model != null) {
173             pomInfo = new PomInfo();
174             pomInfo.setArtifactId(model.getArtifactId());
175             pomInfo.setGroupId(model.getGroupId());
176 
177             Parent parent = model.getParent();
178             if (parent != null) {
179                 pomInfo.setParentArtifactId(parent.getArtifactId());
180                 pomInfo.setParentGroupId(parent.getGroupId());
181             }
182 
183             pomInfo.setFileName(pomFile.getName());
184         }
185         return pomInfo;
186     }
187 
188     /***
189      * Data container which helds information about a pom.
190      * Information may partially be empty.
191      */
192     private static class PomInfo {
193         private String fileName;
194         private String artifactId;
195         private String groupId;
196         private String parentArtifactId;
197         private String parentGroupId;
198 
199         public String getFileName() {
200             return fileName;
201         }
202 
203         public void setFileName(String fileName) {
204             this.fileName = fileName;
205         }
206 
207         public void setArtifactId(String artifactId) {
208             this.artifactId = artifactId;
209         }
210 
211         public void setGroupId(String groupId) {
212             this.groupId = groupId;
213         }
214 
215         public void setParentArtifactId(String parentArtifactId) {
216             this.parentArtifactId = parentArtifactId;
217         }
218 
219         public void setParentGroupId(String parentGroupId) {
220             this.parentGroupId = parentGroupId;
221         }
222 
223         @Override
224         public boolean equals(Object o) {
225             if (this == o) {
226                 return true;
227             }
228 
229             if (o == null || getClass() != o.getClass()) {
230                 return false;
231             }
232 
233             PomInfo pomInfo = (PomInfo) o;
234 
235             if (artifactId != null ? !artifactId.equals(pomInfo.artifactId) : pomInfo.artifactId != null) {
236                 return false;
237             }
238             if (groupId != null ? !groupId.equals(pomInfo.groupId) : pomInfo.groupId != null) {
239                 return false;
240             }
241             if (parentArtifactId != null
242                     ? !parentArtifactId.equals(pomInfo.parentArtifactId)
243                     : pomInfo.parentArtifactId != null) {
244                 return false;
245             }
246             if (parentGroupId != null ? !parentGroupId.equals(pomInfo.parentGroupId) : pomInfo.parentGroupId != null) {
247                 return false;
248             }
249 
250             return true;
251         }
252 
253         @Override
254         public int hashCode() {
255             int result = artifactId != null ? artifactId.hashCode() : 0;
256             result = 31 * result + (groupId != null ? groupId.hashCode() : 0);
257             result = 31 * result + (parentArtifactId != null ? parentArtifactId.hashCode() : 0);
258             result = 31 * result + (parentGroupId != null ? parentGroupId.hashCode() : 0);
259             return result;
260         }
261     }
262 }