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