1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.buildcache;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.nio.file.FileSystems;
24 import java.nio.file.FileVisitResult;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.PathMatcher;
28 import java.nio.file.SimpleFileVisitor;
29 import java.nio.file.StandardCopyOption;
30 import java.nio.file.attribute.BasicFileAttributes;
31 import java.nio.file.attribute.FileTime;
32 import java.nio.file.attribute.PosixFilePermission;
33 import java.util.Arrays;
34 import java.util.Collection;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.NoSuchElementException;
38 import java.util.Set;
39 import java.util.stream.Stream;
40
41 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
42 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
43 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
44 import org.apache.commons.lang3.StringUtils;
45 import org.apache.commons.lang3.Strings;
46 import org.apache.commons.lang3.mutable.MutableBoolean;
47 import org.apache.maven.artifact.Artifact;
48 import org.apache.maven.artifact.handler.ArtifactHandler;
49 import org.apache.maven.buildcache.xml.build.Scm;
50 import org.apache.maven.execution.MavenSession;
51 import org.apache.maven.model.Dependency;
52 import org.apache.maven.plugin.MojoExecution;
53 import org.apache.maven.project.MavenProject;
54 import org.slf4j.Logger;
55
56 import static org.apache.maven.artifact.Artifact.LATEST_VERSION;
57 import static org.apache.maven.artifact.Artifact.SNAPSHOT_VERSION;
58
59
60
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 String.join(
96 ":",
97 Arrays.asList(
98 StringUtils.defaultIfEmpty(mojo.getExecutionId(), "emptyExecId"),
99 StringUtils.defaultIfEmpty(mojo.getGoal(), "emptyGoal"),
100 StringUtils.defaultIfEmpty(mojo.getLifecyclePhase(), "emptyLifecyclePhase"),
101 StringUtils.defaultIfEmpty(mojo.getArtifactId(), "emptyArtifactId"),
102 StringUtils.defaultIfEmpty(mojo.getGroupId(), "emptyGroupId")));
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 = Strings.CS.removeStart(headRef, "ref: ").trim();
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(revision.trim());
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 try (Stream<String> lines = Files.lines(path)) {
135 return lines.findFirst().orElse(defaultValue);
136 }
137 }
138
139 public static <T> T getLast(List<T> list) {
140 int size = list.size();
141 if (size > 0) {
142 return list.get(size - 1);
143 }
144 throw new NoSuchElementException();
145 }
146
147 public static boolean isArchive(File file) {
148 String fileName = file.getName();
149 if (!file.isFile() || file.isHidden()) {
150 return false;
151 }
152 return Strings.CS.endsWithAny(fileName, ".jar", ".zip", ".war", ".ear");
153 }
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 public static boolean zip(final Path dir, final Path zip, final String glob, boolean preservePermissions)
170 throws IOException {
171 final MutableBoolean hasFiles = new MutableBoolean();
172
173 final boolean supportsPosix = preservePermissions
174 && dir.getFileSystem().supportedFileAttributeViews().contains("posix");
175
176 try (ZipArchiveOutputStream zipOutputStream = new ZipArchiveOutputStream(Files.newOutputStream(zip))) {
177
178 PathMatcher matcher =
179 "*".equals(glob) ? null : FileSystems.getDefault().getPathMatcher("glob:" + glob);
180 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
181
182 @Override
183 public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes)
184 throws IOException {
185
186 if (matcher == null || matcher.matches(path.getFileName())) {
187 final ZipArchiveEntry zipEntry =
188 new ZipArchiveEntry(dir.relativize(path).toString());
189
190
191 if (supportsPosix) {
192 Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path);
193 zipEntry.setUnixMode(permissionsToMode(permissions));
194 }
195
196 zipOutputStream.putArchiveEntry(zipEntry);
197 Files.copy(path, zipOutputStream);
198 hasFiles.setTrue();
199 zipOutputStream.closeArchiveEntry();
200 }
201 return FileVisitResult.CONTINUE;
202 }
203 });
204 }
205 return hasFiles.booleanValue();
206 }
207
208 public static void unzip(Path zip, Path out, boolean preservePermissions) throws IOException {
209
210 final boolean supportsPosix = preservePermissions
211 && out.getFileSystem().supportedFileAttributeViews().contains("posix");
212
213 try (ZipArchiveInputStream zis = new ZipArchiveInputStream(Files.newInputStream(zip))) {
214 ZipArchiveEntry entry = zis.getNextEntry();
215 while (entry != null) {
216 Path file = out.resolve(entry.getName());
217 if (!file.normalize().startsWith(out.normalize())) {
218 throw new RuntimeException("Bad zip entry");
219 }
220 if (entry.isDirectory()) {
221 Files.createDirectory(file);
222 } else {
223 Path parent = file.getParent();
224 Files.createDirectories(parent);
225 Files.copy(zis, file, StandardCopyOption.REPLACE_EXISTING);
226 }
227 Files.setLastModifiedTime(file, FileTime.fromMillis(entry.getTime()));
228
229
230 if (supportsPosix) {
231 int unixMode = entry.getUnixMode();
232 if (unixMode != 0) {
233 Set<PosixFilePermission> permissions = modeToPermissions(unixMode);
234 Files.setPosixFilePermissions(file, permissions);
235 }
236 }
237
238 entry = zis.getNextEntry();
239 }
240 }
241 }
242
243 public static <T> void debugPrintCollection(
244 Logger logger, Collection<T> values, String heading, String elementCaption) {
245 if (logger.isDebugEnabled() && values != null && !values.isEmpty()) {
246 final int size = values.size();
247 int i = 0;
248 logger.debug("{} (total {})", heading, size);
249 for (T value : values) {
250 i++;
251 logger.debug("{} {} of {} : {}", elementCaption, i, size, value);
252 }
253 }
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268 private static int permissionsToMode(Set<PosixFilePermission> permissions) {
269
270
271 if (permissions.contains(PosixFilePermission.OWNER_EXECUTE)) {
272 return 0100755;
273 } else {
274 return 0100644;
275 }
276 }
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 private static Set<PosixFilePermission> modeToPermissions(int mode) {
294 Set<PosixFilePermission> permissions = new HashSet<>();
295
296
297 if ((mode & 0100) != 0) {
298
299 permissions.add(PosixFilePermission.OWNER_READ);
300 permissions.add(PosixFilePermission.OWNER_WRITE);
301 permissions.add(PosixFilePermission.OWNER_EXECUTE);
302 permissions.add(PosixFilePermission.GROUP_READ);
303 permissions.add(PosixFilePermission.GROUP_EXECUTE);
304 permissions.add(PosixFilePermission.OTHERS_READ);
305 permissions.add(PosixFilePermission.OTHERS_EXECUTE);
306 } else {
307
308 permissions.add(PosixFilePermission.OWNER_READ);
309 permissions.add(PosixFilePermission.OWNER_WRITE);
310 permissions.add(PosixFilePermission.GROUP_READ);
311 permissions.add(PosixFilePermission.OTHERS_READ);
312 }
313 return permissions;
314 }
315 }