1 package org.apache.maven.plugins.artifact.buildinfo;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.Artifact;
23 import org.apache.maven.artifact.factory.ArtifactFactory;
24 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
25 import org.apache.maven.plugin.MojoExecutionException;
26 import org.apache.maven.plugin.logging.Log;
27 import org.apache.maven.project.MavenProject;
28 import org.apache.maven.shared.utils.io.IOUtil;
29 import org.apache.maven.shared.utils.io.FileUtils;
30 import org.eclipse.aether.AbstractForwardingRepositorySystemSession;
31 import org.eclipse.aether.RepositorySystem;
32 import org.eclipse.aether.RepositorySystemSession;
33 import org.eclipse.aether.artifact.DefaultArtifact;
34 import org.eclipse.aether.repository.RemoteRepository;
35 import org.eclipse.aether.repository.WorkspaceReader;
36 import org.eclipse.aether.resolution.ArtifactRequest;
37 import org.eclipse.aether.resolution.ArtifactResult;
38
39 import java.io.BufferedWriter;
40 import java.io.File;
41 import java.io.FileOutputStream;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.OutputStreamWriter;
45 import java.io.PrintWriter;
46 import java.nio.charset.StandardCharsets;
47 import java.util.Collections;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.Map;
51 import java.util.Set;
52 import java.util.jar.Attributes;
53 import java.util.jar.JarFile;
54 import java.util.jar.Manifest;
55 import java.util.zip.ZipEntry;
56
57
58
59
60 class ReferenceBuildinfoUtil
61 {
62 private static final Set<String> JAR_TYPES;
63
64 static
65 {
66 Set<String> types = new HashSet<>();
67 types.add( "jar" );
68 types.add( "test-jar" );
69 types.add( "war" );
70 types.add( "ear" );
71 types.add( "rar" );
72 types.add( "maven-plugin" );
73 JAR_TYPES = Collections.unmodifiableSet( types );
74 }
75
76 private final Log log;
77
78
79
80
81 private final File referenceDir;
82
83 private final Map<Artifact, String> artifacts;
84
85 private final ArtifactFactory artifactFactory;
86
87 private final RepositorySystem repoSystem;
88
89 private final RepositorySystemSession repoSession;
90
91 ReferenceBuildinfoUtil( Log log, File referenceDir, Map<Artifact, String> artifacts,
92 ArtifactFactory artifactFactory, RepositorySystem repoSystem,
93 RepositorySystemSession repoSession )
94 {
95 this.log = log;
96 this.referenceDir = referenceDir;
97 this.artifacts = artifacts;
98 this.artifactFactory = artifactFactory;
99 this.repoSystem = repoSystem;
100 this.repoSession = repoSession;
101 }
102
103 File downloadOrCreateReferenceBuildinfo( RemoteRepository repo, MavenProject project, File buildinfoFile,
104 boolean mono )
105 throws MojoExecutionException
106 {
107 File referenceBuildinfo = downloadReferenceBuildinfo( repo, project );
108
109 if ( referenceBuildinfo == null )
110 {
111
112 String javaVersion = null;
113 String osName = null;
114 Map<Artifact, File> referenceArtifacts = new HashMap<>();
115 for ( Artifact artifact : artifacts.keySet() )
116 {
117 try
118 {
119
120 File file = downloadReference( repo, artifact );
121 referenceArtifacts.put( artifact, file );
122
123
124 if ( ( javaVersion == null ) && JAR_TYPES.contains( artifact.getType() ) )
125 {
126 log.debug( "Guessing java.version and os.name from jar " + file );
127 try ( JarFile jar = new JarFile( file ) )
128 {
129 Manifest manifest = jar.getManifest();
130 if ( manifest != null )
131 {
132 javaVersion = extractJavaVersion( manifest );
133 osName = extractOsName( artifact, jar );
134 }
135 else
136 {
137 log.warn( "no MANIFEST.MF found in jar " + file );
138 }
139 }
140 catch ( IOException e )
141 {
142 log.warn( "unable to open jar file " + file, e );
143 }
144 }
145 }
146 catch ( ArtifactNotFoundException e )
147 {
148 log.warn( "Reference artifact not found " + artifact );
149 }
150 }
151
152
153 referenceBuildinfo = getReference( buildinfoFile );
154 try ( PrintWriter p =
155 new PrintWriter( new BufferedWriter( new OutputStreamWriter( new FileOutputStream( referenceBuildinfo ),
156 StandardCharsets.UTF_8 ) ) ) )
157 {
158 BuildInfoWriter bi = new BuildInfoWriter( log, p, mono );
159
160 if ( javaVersion != null || osName != null )
161 {
162 p.println( "# effective build environment information" );
163 if ( javaVersion != null )
164 {
165 p.println( "java.version=" + javaVersion );
166 log.info( "Reference build java.version: " + javaVersion );
167 }
168 if ( osName != null )
169 {
170 p.println( "os.name=" + osName );
171 log.info( "Reference build os.name: " + osName );
172
173
174 String expectedLs = osName.startsWith( "Windows" ) ? "\r\n" : "\n";
175 if ( !expectedLs.equals( System.lineSeparator() ) )
176 {
177 log.warn( "Current System.lineSeparator() does not match reference build OS" );
178
179 String ls = System.getProperty( "line.separator" );
180 if ( !ls.equals( System.lineSeparator() ) )
181 {
182 log.warn( "System.lineSeparator() != System.getProperty( \"line.separator\" ): "
183 + "too late standard system property update..." );
184 }
185 }
186 }
187 }
188
189 for ( Map.Entry<Artifact, String> entry : artifacts.entrySet() )
190 {
191 Artifact artifact = entry.getKey();
192 String prefix = entry.getValue();
193 File referenceFile = referenceArtifacts.get( artifact );
194 if ( referenceFile != null )
195 {
196 bi.printFile( prefix, referenceFile );
197 }
198 }
199
200 if ( p.checkError() )
201 {
202 throw new MojoExecutionException( "Write error to " + referenceBuildinfo );
203 }
204
205 log.info( "Minimal buildinfo generated from downloaded artifacts: " + referenceBuildinfo );
206 }
207 catch ( IOException e )
208 {
209 throw new MojoExecutionException( "Error creating file " + referenceBuildinfo, e );
210 }
211 }
212
213 return referenceBuildinfo;
214 }
215
216 private String extractJavaVersion( Manifest manifest )
217 {
218 Attributes attr = manifest.getMainAttributes();
219
220 String value = attr.getValue( "Build-Jdk-Spec" );
221 if ( value != null )
222 {
223 return value + " (from MANIFEST.MF Build-Jdk-Spec)";
224 }
225
226 value = attr.getValue( "Build-Jdk" );
227 if ( value != null )
228 {
229 return String.valueOf( value ) + " (from MANIFEST.MF Build-Jdk)";
230 }
231
232 return null;
233 }
234
235 private String extractOsName( Artifact a, JarFile jar )
236 {
237 String entryName = "META-INF/maven/" + a.getGroupId() + '/' + a.getArtifactId() + "/pom.properties";
238 ZipEntry zipEntry = jar.getEntry( entryName );
239 if ( zipEntry == null )
240 {
241 return null;
242 }
243 try ( InputStream in = jar.getInputStream( zipEntry ) )
244 {
245 String content = IOUtil.toString( in, StandardCharsets.UTF_8.name() );
246 log.debug( "Manifest content: " + content );
247 if ( content.contains( "\r\n" ) )
248 {
249 return "Windows (from pom.properties newline)";
250 }
251 else if ( content.contains( "\n" ) )
252 {
253 return "Unix (from pom.properties newline)";
254 }
255 }
256 catch ( IOException e )
257 {
258 log.warn( "Unable to read " + entryName + " from " + jar, e );
259 }
260 return null;
261 }
262
263 private File downloadReferenceBuildinfo( RemoteRepository repo, MavenProject project )
264 throws MojoExecutionException
265 {
266 Artifact buildinfo =
267 artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(),
268 project.getVersion(), "buildinfo", "" );
269 try
270 {
271 File file = downloadReference( repo, buildinfo );
272
273 log.info( "Reference buildinfo file found, copied to " + file );
274
275 return file;
276 }
277 catch ( ArtifactNotFoundException e )
278 {
279 log.warn( "Reference buildinfo file not found: "
280 + "it will be generated from downloaded reference artifacts" );
281 }
282
283 return null;
284 }
285
286 private File downloadReference( RemoteRepository repo, Artifact artifact )
287 throws MojoExecutionException, ArtifactNotFoundException
288 {
289 try
290 {
291 ArtifactRequest request = new ArtifactRequest();
292 request.setArtifact( new DefaultArtifact( artifact.getGroupId(), artifact.getArtifactId(),
293 artifact.getClassifier(),
294 artifact.getArtifactHandler().getExtension(),
295 artifact.getVersion() ) );
296 request.setRepositories( Collections.singletonList( repo ) );
297
298 ArtifactResult result =
299 repoSystem.resolveArtifact( new NoWorkspaceRepositorySystemSession( repoSession ), request );
300 File resultFile = result.getArtifact().getFile();
301 File destFile = getReference( resultFile );
302
303 FileUtils.copyFile( resultFile, destFile );
304
305 return destFile;
306 }
307 catch ( org.eclipse.aether.resolution.ArtifactResolutionException are )
308 {
309 if ( are.getResult().isMissing() )
310 {
311 throw new ArtifactNotFoundException( "Artifact not found " + artifact, artifact );
312 }
313 throw new MojoExecutionException( "Error resolving reference artifact " + artifact, are );
314 }
315 catch ( IOException ioe )
316 {
317 throw new MojoExecutionException( "Error copying reference artifact " + artifact, ioe );
318 }
319 }
320
321 private File getReference( File file )
322 {
323 return new File( referenceDir, file.getName() );
324 }
325
326 private static class NoWorkspaceRepositorySystemSession
327 extends AbstractForwardingRepositorySystemSession
328 {
329 private final RepositorySystemSession rss;
330
331 NoWorkspaceRepositorySystemSession( RepositorySystemSession rss )
332 {
333 this.rss = rss;
334 }
335
336 @Override
337 protected RepositorySystemSession getSession()
338 {
339 return rss;
340 }
341
342 @Override
343 public WorkspaceReader getWorkspaceReader()
344 {
345 return null;
346 }
347 }
348 }