1 package org.apache.maven.project.artifact;
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.deployer.ArtifactDeploymentException;
24 import org.apache.maven.artifact.installer.ArtifactInstallationException;
25 import org.apache.maven.artifact.repository.ArtifactRepository;
26 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
27 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
28 import org.apache.maven.artifact.transform.ArtifactTransformation;
29 import org.apache.maven.model.Model;
30 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
31 import org.apache.maven.project.DefaultProjectBuilderConfiguration;
32 import org.apache.maven.project.MavenProject;
33 import org.apache.maven.project.ProjectBuilderConfiguration;
34 import org.apache.maven.project.interpolation.ModelInterpolationException;
35 import org.apache.maven.project.interpolation.StringSearchModelInterpolator;
36 import org.codehaus.plexus.interpolation.InterpolationException;
37 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
38 import org.codehaus.plexus.interpolation.Interpolator;
39 import org.codehaus.plexus.interpolation.RecursionInterceptor;
40 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
41 import org.codehaus.plexus.interpolation.ValueSource;
42 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
43 import org.codehaus.plexus.util.IOUtil;
44 import org.codehaus.plexus.util.ReaderFactory;
45 import org.codehaus.plexus.util.WriterFactory;
46 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
47
48 import java.io.File;
49 import java.io.IOException;
50 import java.io.Reader;
51 import java.io.Writer;
52 import java.util.ArrayList;
53 import java.util.Iterator;
54 import java.util.List;
55
56 public class VersionExpressionTransformation
57 extends StringSearchModelInterpolator
58 implements Initializable, ArtifactTransformation
59 {
60
61 public void transformForDeployment( Artifact artifact, ArtifactRepository remoteRepository,
62 ArtifactRepository localRepository )
63 throws ArtifactDeploymentException
64 {
65 ProjectArtifactMetadata metadata = ArtifactWithProject.getProjectArtifactMetadata( artifact );
66 File pomFile;
67 boolean pomArtifact = false;
68 if ( "pom".equals( artifact.getType() ) )
69 {
70 if ( getLogger().isDebugEnabled() )
71 {
72 getLogger().debug( "On Deploy: Using artifact file for POM: " + artifact );
73 }
74 pomFile = artifact.getFile();
75 pomArtifact = true;
76 }
77
78
79
80
81
82
83 else if ( metadata != null )
84 {
85 pomFile = metadata.getFile();
86 }
87 else
88 {
89 return;
90 }
91
92 try
93 {
94 File outFile = transformVersions( pomFile, artifact, localRepository );
95
96 if ( pomArtifact )
97 {
98
99 artifact.setFile( outFile );
100 }
101 else
102 {
103 metadata.setFile( outFile );
104 metadata.setVersionExpressionsResolved( true );
105 }
106 }
107 catch ( IOException e )
108 {
109 throw new ArtifactDeploymentException( "Failed to read or write POM for version transformation.", e );
110 }
111 catch ( ModelInterpolationException e )
112 {
113 throw new ArtifactDeploymentException( "Failed to interpolate POM versions.", e );
114 }
115 }
116
117 public void transformForInstall( Artifact artifact, ArtifactRepository localRepository )
118 throws ArtifactInstallationException
119 {
120 ProjectArtifactMetadata metadata = (ProjectArtifactMetadata) artifact.getMetadata( ProjectArtifactMetadata.class );
121 File pomFile;
122 boolean pomArtifact = false;
123 if ( "pom".equals( artifact.getType() ) )
124 {
125 if ( getLogger().isDebugEnabled() )
126 {
127 getLogger().debug( "On Install: Using artifact file for POM: " + artifact );
128 }
129 pomFile = artifact.getFile();
130 pomArtifact = true;
131 }
132
133
134
135
136
137
138 else if ( metadata != null )
139 {
140 pomFile = metadata.getFile();
141 }
142 else
143 {
144 return;
145 }
146
147 try
148 {
149 File outFile = transformVersions( pomFile, artifact, localRepository );
150
151 if ( pomArtifact )
152 {
153
154 artifact.setFile( outFile );
155 }
156 else
157 {
158 metadata.setFile( outFile );
159 metadata.setVersionExpressionsResolved( true );
160 }
161 }
162 catch ( IOException e )
163 {
164 throw new ArtifactInstallationException( "Failed to read or write POM for version transformation.", e );
165 }
166 catch ( ModelInterpolationException e )
167 {
168 throw new ArtifactInstallationException( "Failed to interpolate POM versions.", e );
169 }
170 }
171
172 public void transformForResolve( Artifact artifact, List remoteRepositories, ArtifactRepository localRepository )
173 throws ArtifactResolutionException, ArtifactNotFoundException
174 {
175 return;
176 }
177
178 protected File transformVersions( File pomFile, Artifact artifact, ArtifactRepository localRepository )
179 throws IOException, ModelInterpolationException
180 {
181 ProjectBuilderConfiguration pbConfig;
182 File projectDir;
183 File outputFile;
184 if ( artifact instanceof ArtifactWithProject )
185 {
186 MavenProject project = ( (ArtifactWithProject) artifact ).getProject();
187
188 projectDir = project.getBasedir();
189 pbConfig = project.getProjectBuilderConfiguration();
190 outputFile = new File( project.getBuild().getDirectory(), "pom-transformed.xml" );
191 }
192 else
193 {
194 if ( getLogger().isDebugEnabled() )
195 {
196 getLogger().debug(
197 "WARNING: Artifact: " + artifact
198 + " does not have project-builder metadata (ProjectBuilderConfiguration) associated with it.\n"
199 + "Cannot access CLI properties for version transformation." );
200 }
201
202 pbConfig = new DefaultProjectBuilderConfiguration().setLocalRepository( localRepository );
203 projectDir = pomFile.getAbsoluteFile().getParentFile();
204 outputFile = new File( projectDir, "target/pom-transformed.xml" );
205 }
206
207 Reader reader = null;
208 Model model;
209 try
210 {
211 reader = ReaderFactory.newXmlReader( pomFile );
212 model = new MavenXpp3Reader().read( reader );
213
214 interpolateVersions( pomFile, outputFile, model, projectDir, pbConfig );
215 }
216 catch ( XmlPullParserException e )
217 {
218 String message =
219 "Failed to parse POM for version transformation. Proceeding with original (non-interpolated) POM file.";
220
221 String detail = "\n\nNOTE: Error was in file: " + pomFile + ", at line: "
222 + e.getLineNumber() + ", column: " + e.getColumnNumber();
223
224 if ( getLogger().isDebugEnabled() )
225 {
226 getLogger().debug( message + detail, e );
227 }
228 else
229 {
230 getLogger().warn( message + " See debug output for details." );
231 }
232
233 outputFile = pomFile;
234 }
235 finally
236 {
237 IOUtil.close( reader );
238 }
239
240 return outputFile;
241 }
242
243 protected void interpolateVersions( File pomFile, File outputFile, Model model, File projectDir, ProjectBuilderConfiguration config )
244 throws ModelInterpolationException
245 {
246 boolean debugEnabled = getLogger().isDebugEnabled();
247
248
249
250
251
252 Interpolator interpolator = new StringSearchInterpolator( "<version>", "</version>" );
253
254
255
256 Interpolator secondaryInterpolator = getInterpolator();
257
258
259 RecursionInterceptor recursionInterceptor = getRecursionInterceptor();
260
261
262
263 interpolator.addValueSource( new SecondaryInterpolationValueSource( secondaryInterpolator, recursionInterceptor ) );
264
265
266
267
268 interpolator.addPostProcessor( new VersionRestoringPostProcessor() );
269
270 List valueSources = createValueSources( model, projectDir, config );
271 List postProcessors = createPostProcessors( model, projectDir, config );
272
273 synchronized ( this )
274 {
275 for ( Iterator it = valueSources.iterator(); it.hasNext(); )
276 {
277 ValueSource vs = (ValueSource) it.next();
278 secondaryInterpolator.addValueSource( vs );
279 }
280
281 for ( Iterator it = postProcessors.iterator(); it.hasNext(); )
282 {
283 InterpolationPostProcessor postProcessor = (InterpolationPostProcessor) it.next();
284
285 secondaryInterpolator.addPostProcessor( postProcessor );
286 }
287
288 String pomContents;
289 try
290 {
291 Reader reader = null;
292 try
293 {
294 reader = ReaderFactory.newXmlReader( pomFile );
295 pomContents = IOUtil.toString( reader );
296 }
297 catch ( IOException e )
298 {
299 throw new ModelInterpolationException( "Error reading POM for version-expression interpolation: " + e.getMessage(), e );
300 }
301 finally
302 {
303 IOUtil.close( reader );
304 }
305
306 try
307 {
308 pomContents = interpolator.interpolate( pomContents );
309 }
310 catch ( InterpolationException e )
311 {
312 throw new ModelInterpolationException( e.getMessage(), e );
313 }
314
315 if ( debugEnabled )
316 {
317 List feedback = interpolator.getFeedback();
318 if ( feedback != null && !feedback.isEmpty() )
319 {
320 getLogger().debug( "Maven encountered the following problems while transforming POM versions:" );
321
322 Object last = null;
323 for ( Iterator it = feedback.iterator(); it.hasNext(); )
324 {
325 Object next = it.next();
326
327 if ( next instanceof Throwable )
328 {
329 if ( last == null )
330 {
331 getLogger().debug( "", ( (Throwable) next ) );
332 }
333 else
334 {
335 getLogger().debug( String.valueOf( last ), ( (Throwable) next ) );
336 }
337 }
338 else
339 {
340 if ( last != null )
341 {
342 getLogger().debug( String.valueOf( last ) );
343 }
344
345 last = next;
346 }
347 }
348
349 if ( last != null )
350 {
351 getLogger().debug( String.valueOf( last ) );
352 }
353 }
354 }
355
356 interpolator.clearFeedback();
357 }
358 finally
359 {
360 for ( Iterator iterator = valueSources.iterator(); iterator.hasNext(); )
361 {
362 ValueSource vs = (ValueSource) iterator.next();
363 secondaryInterpolator.removeValuesSource( vs );
364 }
365
366 for ( Iterator iterator = postProcessors.iterator(); iterator.hasNext(); )
367 {
368 InterpolationPostProcessor postProcessor = (InterpolationPostProcessor) iterator.next();
369 secondaryInterpolator.removePostProcessor( postProcessor );
370 }
371
372 getInterpolator().clearAnswers();
373 }
374
375 Writer writer = null;
376 try
377 {
378 outputFile.getParentFile().mkdirs();
379
380 writer = WriterFactory.newXmlWriter( outputFile );
381
382 IOUtil.copy( pomContents, writer );
383 }
384 catch ( IOException e )
385 {
386 throw new ModelInterpolationException( "Failed to write transformed POM: " + outputFile.getAbsolutePath(), e );
387 }
388 finally
389 {
390 IOUtil.close( writer );
391 }
392 }
393
394
395
396
397
398 }
399
400 private static final class SecondaryInterpolationValueSource
401 implements ValueSource
402 {
403
404 private Interpolator secondary;
405 private final RecursionInterceptor recursionInterceptor;
406 private List localFeedback = new ArrayList();
407
408 public SecondaryInterpolationValueSource( Interpolator secondary, RecursionInterceptor recursionInterceptor )
409 {
410 this.secondary = secondary;
411 this.recursionInterceptor = recursionInterceptor;
412 }
413
414 public void clearFeedback()
415 {
416 secondary.clearFeedback();
417 }
418
419 public List getFeedback()
420 {
421 List result = secondary.getFeedback();
422 if ( result != null )
423 {
424 result = new ArrayList( result );
425 }
426
427 result.addAll( localFeedback );
428
429 return result;
430 }
431
432 public Object getValue( String expression )
433 {
434 try
435 {
436 return secondary.interpolate( expression, recursionInterceptor );
437 }
438 catch ( InterpolationException e )
439 {
440 localFeedback.add( "Error during version expression interpolation." );
441 localFeedback.add( e );
442 }
443
444 return null;
445 }
446 }
447
448 private static final class VersionRestoringPostProcessor
449 implements InterpolationPostProcessor
450 {
451
452 public Object execute( String expression, Object value )
453 {
454 return "<version>" + value + "</version>";
455 }
456
457 }
458
459 }