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.buildcache;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.nio.charset.StandardCharsets;
24  import java.nio.file.FileSystems;
25  import java.nio.file.FileVisitResult;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.nio.file.PathMatcher;
29  import java.nio.file.SimpleFileVisitor;
30  import java.nio.file.StandardCopyOption;
31  import java.nio.file.attribute.BasicFileAttributes;
32  import java.nio.file.attribute.FileTime;
33  import java.util.Arrays;
34  import java.util.Collection;
35  import java.util.List;
36  import java.util.NoSuchElementException;
37  import java.util.function.Supplier;
38  import java.util.zip.ZipEntry;
39  import java.util.zip.ZipInputStream;
40  import java.util.zip.ZipOutputStream;
41  
42  import org.apache.commons.lang3.StringUtils;
43  import org.apache.commons.lang3.mutable.MutableBoolean;
44  import org.apache.maven.artifact.Artifact;
45  import org.apache.maven.artifact.handler.ArtifactHandler;
46  import org.apache.maven.buildcache.xml.build.Scm;
47  import org.apache.maven.execution.MavenSession;
48  import org.apache.maven.model.Dependency;
49  import org.apache.maven.plugin.MojoExecution;
50  import org.apache.maven.project.MavenProject;
51  import org.eclipse.aether.SessionData;
52  import org.slf4j.Logger;
53  
54  import static org.apache.commons.lang3.StringUtils.removeStart;
55  import static org.apache.commons.lang3.StringUtils.trim;
56  import static org.apache.maven.artifact.Artifact.LATEST_VERSION;
57  import static org.apache.maven.artifact.Artifact.SNAPSHOT_VERSION;
58  
59  /**
60   * Cache Utils
61   */
62  public class CacheUtils {
63  
64      public static boolean isPom(MavenProject project) {
65          return project.getPackaging().equals("pom");
66      }
67  
68      public static boolean isPom(Dependency dependency) {
69          return dependency.getType().equals("pom");
70      }
71  
72      public static boolean isSnapshot(String version) {
73          return version.endsWith(SNAPSHOT_VERSION) || version.endsWith(LATEST_VERSION);
74      }
75  
76      public static String normalizedName(Artifact artifact) {
77          if (artifact.getFile() == null) {
78              return null;
79          }
80  
81          StringBuilder filename = new StringBuilder(artifact.getArtifactId());
82  
83          if (artifact.hasClassifier()) {
84              filename.append("-").append(artifact.getClassifier());
85          }
86  
87          final ArtifactHandler artifactHandler = artifact.getArtifactHandler();
88          if (artifactHandler != null && StringUtils.isNotBlank(artifactHandler.getExtension())) {
89              filename.append(".").append(artifactHandler.getExtension());
90          }
91          return filename.toString();
92      }
93  
94      public static String mojoExecutionKey(MojoExecution mojo) {
95          return StringUtils.join(
96                  Arrays.asList(
97                          StringUtils.defaultIfEmpty(mojo.getExecutionId(), "emptyExecId"),
98                          StringUtils.defaultIfEmpty(mojo.getGoal(), "emptyGoal"),
99                          StringUtils.defaultIfEmpty(mojo.getLifecyclePhase(), "emptyLifecyclePhase"),
100                         StringUtils.defaultIfEmpty(mojo.getArtifactId(), "emptyArtifactId"),
101                         StringUtils.defaultIfEmpty(mojo.getGroupId(), "emptyGroupId")),
102                 ":");
103     }
104 
105     public static Path getMultimoduleRoot(MavenSession session) {
106         return session.getRequest().getMultiModuleProjectDirectory().toPath();
107     }
108 
109     public static Scm readGitInfo(MavenSession session) throws IOException {
110         final Scm scmCandidate = new Scm();
111         final Path gitDir = getMultimoduleRoot(session).resolve(".git");
112         if (Files.isDirectory(gitDir)) {
113             final Path headFile = gitDir.resolve("HEAD");
114             if (Files.exists(headFile)) {
115                 String headRef = readFirstLine(headFile, "<missing branch>");
116                 if (headRef.startsWith("ref: ")) {
117                     String branch = trim(removeStart(headRef, "ref: "));
118                     scmCandidate.setSourceBranch(branch);
119                     final Path refPath = gitDir.resolve(branch);
120                     if (Files.exists(refPath)) {
121                         String revision = readFirstLine(refPath, "<missing revision>");
122                         scmCandidate.setRevision(trim(revision));
123                     }
124                 } else {
125                     scmCandidate.setSourceBranch(headRef);
126                     scmCandidate.setRevision(headRef);
127                 }
128             }
129         }
130         return scmCandidate;
131     }
132 
133     private static String readFirstLine(Path path, String defaultValue) throws IOException {
134         return Files.lines(path, StandardCharsets.UTF_8).findFirst().orElse(defaultValue);
135     }
136 
137     public static <T> T getLast(List<T> list) {
138         int size = list.size();
139         if (size > 0) {
140             return list.get(size - 1);
141         }
142         throw new NoSuchElementException();
143     }
144 
145     public static <T> T getOrCreate(MavenSession session, Object key, Supplier<T> supplier) {
146         SessionData data = session.getRepositorySession().getData();
147         while (true) {
148             T t = (T) data.get(key);
149             if (t == null) {
150                 t = supplier.get();
151                 if (data.set(key, null, t)) {
152                     continue;
153                 }
154             }
155             return t;
156         }
157     }
158 
159     public static boolean isArchive(File file) {
160         String fileName = file.getName();
161         if (!file.isFile() || file.isHidden()) {
162             return false;
163         }
164         return StringUtils.endsWithAny(fileName, ".jar", ".zip", ".war", ".ear");
165     }
166 
167     /**
168      * Put every matching files of a directory in a zip.
169      * @param dir directory to zip
170      * @param zip zip to populate
171      * @param glob glob to apply to filenames
172      * @return true if at least one file has been included in the zip.
173      * @throws IOException
174      */
175     public static boolean zip(final Path dir, final Path zip, final String glob) throws IOException {
176         final MutableBoolean hasFiles = new MutableBoolean();
177         try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zip))) {
178 
179             PathMatcher matcher =
180                     "*".equals(glob) ? null : FileSystems.getDefault().getPathMatcher("glob:" + glob);
181             Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
182 
183                 @Override
184                 public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes)
185                         throws IOException {
186 
187                     if (matcher == null || matcher.matches(path.getFileName())) {
188                         final ZipEntry zipEntry =
189                                 new ZipEntry(dir.relativize(path).toString());
190                         zipOutputStream.putNextEntry(zipEntry);
191                         Files.copy(path, zipOutputStream);
192                         hasFiles.setTrue();
193                         zipOutputStream.closeEntry();
194                     }
195                     return FileVisitResult.CONTINUE;
196                 }
197             });
198         }
199         return hasFiles.booleanValue();
200     }
201 
202     public static void unzip(Path zip, Path out) throws IOException {
203         try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(zip))) {
204             ZipEntry entry = zis.getNextEntry();
205             while (entry != null) {
206                 Path file = out.resolve(entry.getName());
207                 if (!file.normalize().startsWith(out.normalize())) {
208                     throw new RuntimeException("Bad zip entry");
209                 }
210                 if (entry.isDirectory()) {
211                     Files.createDirectory(file);
212                 } else {
213                     Path parent = file.getParent();
214                     Files.createDirectories(parent);
215                     Files.copy(zis, file, StandardCopyOption.REPLACE_EXISTING);
216                 }
217                 Files.setLastModifiedTime(file, FileTime.fromMillis(entry.getTime()));
218                 entry = zis.getNextEntry();
219             }
220         }
221     }
222 
223     public static <T> void debugPrintCollection(
224             Logger logger, Collection<T> values, String heading, String elementCaption) {
225         if (logger.isDebugEnabled() && values != null && !values.isEmpty()) {
226             final int size = values.size();
227             int i = 0;
228             logger.debug("{} (total {})", heading, size);
229             for (T value : values) {
230                 i++;
231                 logger.debug("{} {} of {} : {}", elementCaption, i, size, value);
232             }
233         }
234     }
235 }