1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.wrapper;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.URI;
24 import java.nio.file.DirectoryStream;
25 import java.nio.file.FileVisitResult;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
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.PosixFilePermission;
33 import java.nio.file.attribute.PosixFilePermissions;
34 import java.util.ArrayList;
35 import java.util.Enumeration;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Set;
39 import java.util.zip.ZipEntry;
40 import java.util.zip.ZipException;
41 import java.util.zip.ZipFile;
42
43
44
45
46
47
48 public class Installer {
49 public static final Path DEFAULT_DISTRIBUTION_PATH = Paths.get("wrapper", "dists");
50
51 private final Downloader download;
52
53 private final Verifier verifier;
54
55 private final PathAssembler pathAssembler;
56
57 public Installer(Downloader download, Verifier verifier, PathAssembler pathAssembler) {
58 this.download = download;
59 this.verifier = verifier;
60 this.pathAssembler = pathAssembler;
61 }
62
63 public Path createDist(WrapperConfiguration configuration) throws Exception {
64 URI distributionUrl = configuration.getDistribution();
65
66 boolean alwaysDownload = configuration.isAlwaysDownload();
67 boolean alwaysUnpack = configuration.isAlwaysUnpack();
68 boolean verifyDistributionSha256Sum =
69 !configuration.getDistributionSha256Sum().isEmpty();
70
71 PathAssembler.LocalDistribution localDistribution = pathAssembler.getDistribution(configuration);
72 Path localZipFile = localDistribution.getZipFile();
73
74 if (alwaysDownload || alwaysUnpack || Files.notExists(localZipFile)) {
75 Logger.info("Installing Maven distribution "
76 + localDistribution.getDistributionDir().toAbsolutePath());
77 }
78
79 boolean downloaded = false;
80 if (alwaysDownload || Files.notExists(localZipFile)) {
81 Logger.info("Downloading " + distributionUrl);
82 Path tmpZipFile = localZipFile.resolveSibling(localZipFile.getFileName() + ".part");
83 Files.deleteIfExists(tmpZipFile);
84 download.download(distributionUrl, tmpZipFile);
85 Files.move(tmpZipFile, localZipFile, StandardCopyOption.REPLACE_EXISTING);
86 downloaded = Files.exists(localZipFile);
87 }
88
89 Path distDir = localDistribution.getDistributionDir();
90 List<Path> dirs = listDirs(distDir);
91
92 if (downloaded || alwaysUnpack || dirs.isEmpty()) {
93 if (verifyDistributionSha256Sum) {
94 verifier.verify(
95 localZipFile,
96 "distributionSha256Sum",
97 Verifier.SHA_256_ALGORITHM,
98 configuration.getDistributionSha256Sum());
99 }
100 for (Path dir : dirs) {
101 Logger.info("Deleting directory " + dir.toAbsolutePath());
102 deleteDir(dir);
103 }
104 Logger.info("Unzipping " + localZipFile.toAbsolutePath() + " to " + distDir.toAbsolutePath());
105 unzip(localZipFile, distDir);
106 dirs = listDirs(distDir);
107 if (dirs.isEmpty()) {
108 throw new RuntimeException(String.format(
109 Locale.ROOT,
110 "Maven distribution '%s' does not contain any directory."
111 + " Expected to find exactly 1 directory.",
112 distDir));
113 }
114 setExecutablePermissions(dirs.get(0));
115 }
116 if (dirs.size() != 1) {
117 throw new RuntimeException(String.format(
118 Locale.ROOT,
119 "Maven distribution '%s' contains too many directories." + " Expected to find exactly 1 directory.",
120 distDir));
121 }
122 return dirs.get(0);
123 }
124
125 private List<Path> listDirs(Path distDir) throws IOException {
126 List<Path> dirs = new ArrayList<>();
127 if (Files.exists(distDir)) {
128 try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(distDir)) {
129 for (Path file : dirStream) {
130 if (Files.isDirectory(file)) {
131 dirs.add(file);
132 }
133 }
134 }
135 }
136 return dirs;
137 }
138
139 private void setExecutablePermissions(Path mavenHome) {
140 if (isWindows()) {
141 return;
142 }
143 Path mavenCommand = mavenHome.resolve("bin/mvn");
144 try {
145 Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-xr-x");
146 Files.setPosixFilePermissions(mavenCommand, perms);
147 } catch (IOException e) {
148 Logger.warn("Could not set executable permissions for: " + mavenCommand.toAbsolutePath()
149 + ". Please do this manually if you want to use Maven.");
150 }
151 }
152
153 private boolean isWindows() {
154 String osName = System.getProperty("os.name").toLowerCase(Locale.US);
155 return osName.contains("windows");
156 }
157
158 private void deleteDir(Path dirPath) throws IOException {
159 Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
160 @Override
161 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
162 Files.delete(dir);
163 if (exc != null) {
164 throw exc;
165 }
166 return FileVisitResult.CONTINUE;
167 }
168
169 @Override
170 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
171 Files.delete(file);
172 return FileVisitResult.CONTINUE;
173 }
174 });
175 }
176
177 public void unzip(Path zip, Path dest) throws IOException {
178 final Path destDir = dest.normalize();
179 try (ZipFile zipFile = new ZipFile(zip.toFile())) {
180 final Enumeration<? extends ZipEntry> entries = zipFile.entries();
181
182 while (entries.hasMoreElements()) {
183 final ZipEntry entry = entries.nextElement();
184
185 Path fileEntry = destDir.resolve(entry.getName()).normalize();
186 if (!fileEntry.startsWith(destDir)) {
187 throw new ZipException("Zip includes an invalid entry: " + entry.getName());
188 }
189
190 if (entry.isDirectory()) {
191 continue;
192 }
193
194 Files.createDirectories(fileEntry.getParent());
195
196 try (InputStream inStream = zipFile.getInputStream(entry)) {
197 Files.copy(inStream, fileEntry);
198 }
199 }
200 }
201 }
202 }