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  import java.io.InputStream;
24  import java.nio.file.Files;
25  
26  import org.apache.maven.model.Model;
27  import org.apache.maven.model.Parent;
28  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
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       * @param originPom the pom File which should be used as blueprint for the search
74       * @return <code>true</code> if a pom got parsed successfully, <code>false</code> otherwise
75       */
76      public boolean parsePom(File originPom) {
77          if (!originPom.exists()) {
78              return false;
79          }
80  
81          try {
82              foundPomInfo = readPomInfo(originPom);
83          } catch (Exception e) {
84              log.warn("Error while parsing pom file", e);
85              return false;
86          }
87  
88          return foundPomInfo != null;
89      }
90  
91      /**
92       * Search for the previously with {@link #parsePom(java.io.File)}
93       * parsed pom in the given directory.
94       *
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      *
156      * @param pomFile pom.xml file
157      * @return the PomInfo or <code>null</code
158      */
159     private PomInfo readPomInfo(File pomFile) throws IOException, XmlPullParserException {
160         if (!pomFile.exists() || !pomFile.isFile()) {
161             return null;
162         }
163 
164         PomInfo pomInfo = null;
165 
166         MavenXpp3Reader reader = new MavenXpp3Reader();
167 
168         try (InputStream in = Files.newInputStream(pomFile.toPath())) {
169             Model model = reader.read(in);
170 
171             if (model != null) {
172                 pomInfo = new PomInfo();
173                 pomInfo.setArtifactId(model.getArtifactId());
174                 pomInfo.setGroupId(model.getGroupId());
175 
176                 Parent parent = model.getParent();
177                 if (parent != null) {
178                     pomInfo.setParentArtifactId(parent.getArtifactId());
179                     pomInfo.setParentGroupId(parent.getGroupId());
180                 }
181 
182                 pomInfo.setFileName(pomFile.getName());
183             }
184             return pomInfo;
185         }
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 }