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