1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.felix.obrplugin;
20
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStreamReader;
28 import java.net.MalformedURLException;
29 import java.net.URI;
30 import java.net.URL;
31 import java.text.SimpleDateFormat;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Date;
35 import java.util.List;
36 import java.util.Properties;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39
40 import javax.xml.parsers.DocumentBuilder;
41 import javax.xml.parsers.DocumentBuilderFactory;
42 import javax.xml.parsers.ParserConfigurationException;
43 import javax.xml.transform.Result;
44 import javax.xml.transform.Transformer;
45 import javax.xml.transform.TransformerConfigurationException;
46 import javax.xml.transform.TransformerException;
47 import javax.xml.transform.TransformerFactory;
48 import javax.xml.transform.dom.DOMSource;
49 import javax.xml.transform.stream.StreamResult;
50
51 import org.apache.maven.artifact.manager.WagonManager;
52 import org.apache.maven.artifact.repository.ArtifactRepository;
53 import org.apache.maven.plugin.AbstractMojo;
54 import org.apache.maven.plugin.MojoExecutionException;
55 import org.apache.maven.plugin.logging.Log;
56 import org.apache.maven.plugins.annotations.Component;
57 import org.apache.maven.plugins.annotations.LifecyclePhase;
58 import org.apache.maven.plugins.annotations.Mojo;
59 import org.apache.maven.plugins.annotations.Parameter;
60 import org.apache.maven.project.MavenProject;
61 import org.apache.maven.settings.Settings;
62 import org.w3c.dom.Document;
63 import org.w3c.dom.Element;
64 import org.w3c.dom.Node;
65 import org.w3c.dom.NodeList;
66 import org.xml.sax.SAXException;
67
68
69
70
71
72
73
74
75 @Mojo( name = "remote-clean", requiresProject = false, defaultPhase = LifecyclePhase.CLEAN )
76 public final class ObrRemoteClean extends AbstractMojo
77 {
78
79
80
81 @Parameter( property = "ignoreLock" )
82 private boolean ignoreLock;
83
84
85
86
87 @Parameter( property = "prefixUrl" )
88 private String prefixUrl;
89
90
91
92
93 @Parameter( property = "remoteOBR", defaultValue = "NONE" )
94 private String remoteOBR;
95
96
97
98
99 @Parameter( property = "obrRepository" )
100 private String obrRepository;
101
102
103
104
105 @Parameter
106 private List supportedProjectTypes = Arrays.asList( new String[]
107 { "jar", "bundle" } );
108
109 @Parameter( defaultValue = "${project.distributionManagementArtifactRepository}", readonly = true )
110 private ArtifactRepository deploymentRepository;
111
112
113
114
115 @Parameter( property = "altDeploymentRepository" )
116 private String altDeploymentRepository;
117
118
119
120
121 @Parameter( property = "obrDeploymentRepository" )
122 private String obrDeploymentRepository;
123
124 @Parameter( defaultValue = "${settings.interactiveMode}", readonly = true )
125 private boolean interactive;
126
127
128
129
130 @Parameter( defaultValue = "${project}", readonly = true, required = true )
131 private MavenProject project;
132
133
134
135
136 @Parameter( defaultValue = "${settings}", readonly = true, required = true )
137 private Settings settings;
138
139
140
141
142 @Component
143 private WagonManager m_wagonManager;
144
145
146 public void execute() throws MojoExecutionException
147 {
148 String projectType = project.getPackaging();
149
150
151 if ( !supportedProjectTypes.contains( projectType ) )
152 {
153 getLog().warn(
154 "Ignoring project type " + projectType + " - supportedProjectTypes = " + supportedProjectTypes );
155 return;
156 }
157 else if ( "NONE".equalsIgnoreCase( remoteOBR ) || "false".equalsIgnoreCase( remoteOBR ) )
158 {
159 getLog().info( "Remote OBR update disabled (enable with -DremoteOBR)" );
160 return;
161 }
162
163
164 if ( null == remoteOBR || remoteOBR.trim().length() == 0 || "true".equalsIgnoreCase( remoteOBR ) )
165 {
166 remoteOBR = obrRepository;
167 }
168
169 URI tempURI = ObrUtils.findRepositoryXml( "", remoteOBR );
170 String repositoryName = new File( tempURI.getSchemeSpecificPart() ).getName();
171
172 Log log = getLog();
173
174 RemoteFileManager remoteFile = new RemoteFileManager( m_wagonManager, settings, log );
175 openRepositoryConnection( remoteFile );
176 if ( null == prefixUrl )
177 {
178 prefixUrl = remoteFile.toString();
179 }
180
181
182 log.info( "LOCK " + remoteFile + '/' + repositoryName );
183 remoteFile.lockFile( repositoryName, ignoreLock );
184 File downloadedRepositoryXml = null;
185
186 try
187 {
188
189 log.info( "Downloading " + repositoryName );
190 downloadedRepositoryXml = remoteFile.get( repositoryName, ".xml" );
191
192 URI repositoryXml = downloadedRepositoryXml.toURI();
193
194 Config userConfig = new Config();
195 userConfig.setRemoteFile( true );
196
197
198 Document doc = parseFile( new File( repositoryXml ), initConstructor() );
199 Node finalDocument = cleanDocument( doc.getDocumentElement() );
200
201 if ( finalDocument == null )
202 {
203 getLog().info( "Nothing to clean in " + repositoryName );
204 }
205 else
206 {
207 writeToFile( repositoryXml, finalDocument );
208 getLog().info( "Repository " + repositoryName + " cleaned" );
209
210 log.info( "Uploading " + repositoryName );
211 remoteFile.put( downloadedRepositoryXml, repositoryName );
212 }
213 }
214 catch ( Exception e )
215 {
216 log.warn( "Exception while updating remote OBR: " + e.getLocalizedMessage(), e );
217 }
218 finally
219 {
220
221 log.info( "UNLOCK " + remoteFile + '/' + repositoryName );
222 remoteFile.unlockFile( repositoryName );
223 remoteFile.disconnect();
224
225 if ( null != downloadedRepositoryXml )
226 {
227 downloadedRepositoryXml.delete();
228 }
229 }
230 }
231
232 private static final Pattern ALT_REPO_SYNTAX_PATTERN = Pattern.compile( "(.+)::(.+)::(.+)" );
233
234
235 private void openRepositoryConnection( RemoteFileManager remoteFile ) throws MojoExecutionException
236 {
237
238 if ( obrDeploymentRepository != null )
239 {
240 altDeploymentRepository = obrDeploymentRepository;
241 }
242
243 if ( deploymentRepository == null && altDeploymentRepository == null )
244 {
245 String msg = "Deployment failed: repository element was not specified in the pom inside"
246 + " distributionManagement element or in -DaltDeploymentRepository=id::layout::url parameter";
247
248 throw new MojoExecutionException( msg );
249 }
250
251 if ( altDeploymentRepository != null )
252 {
253 getLog().info( "Using alternate deployment repository " + altDeploymentRepository );
254
255 Matcher matcher = ALT_REPO_SYNTAX_PATTERN.matcher( altDeploymentRepository );
256 if ( !matcher.matches() )
257 {
258 throw new MojoExecutionException( "Invalid syntax for alternative repository \""
259 + altDeploymentRepository + "\". Use \"id::layout::url\"." );
260 }
261
262 remoteFile.connect( matcher.group( 1 ).trim(), matcher.group( 3 ).trim() );
263 }
264 else
265 {
266 remoteFile.connect( deploymentRepository.getId(), deploymentRepository.getUrl() );
267 }
268 }
269
270
271
272
273
274
275
276
277 private Element cleanDocument( Element elem )
278 {
279 NodeList nodes = elem.getElementsByTagName( "resource" );
280 List toRemove = new ArrayList();
281
282
283 for ( int i = 0; i < nodes.getLength(); i++ )
284 {
285 Element n = ( Element ) nodes.item( i );
286 String value = n.getAttribute( "uri" );
287
288 URL url;
289 try
290 {
291 url = new URL( new URL( prefixUrl + '/' ), value );
292 }
293 catch ( MalformedURLException e )
294 {
295 getLog().error( "Malformed URL when creating the resource absolute URI : " + e.getMessage() );
296 return null;
297 }
298
299 try
300 {
301 url.openConnection().getContent();
302 }
303 catch ( IOException e )
304 {
305 getLog().info(
306 "The bundle " + n.getAttribute( "presentationname" ) + " - " + n.getAttribute( "version" )
307 + " will be removed : " + e.getMessage() );
308 toRemove.add( n );
309 }
310 }
311
312 Date d = new Date();
313 if ( toRemove.size() > 0 )
314 {
315 String answer = "y";
316 if ( interactive )
317 {
318 System.out.println( "Do you want to remove these bundles from the repository file [y/N]:" );
319 BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
320
321 try
322 {
323 answer = br.readLine();
324 }
325 catch ( IOException ioe )
326 {
327 getLog().error( "IO error trying to read the user confirmation" );
328 return null;
329 }
330 }
331
332 if ( answer != null && answer.trim().equalsIgnoreCase( "y" ) )
333 {
334
335 for ( int i = 0; i < toRemove.size(); i++ )
336 {
337 elem.removeChild( ( Node ) toRemove.get( i ) );
338 }
339
340
341 SimpleDateFormat format = new SimpleDateFormat( "yyyyMMddHHmmss.SSS" );
342 d.setTime( System.currentTimeMillis() );
343 elem.setAttribute( "lastmodified", format.format( d ) );
344 return elem;
345 }
346 else
347 {
348 return null;
349 }
350 }
351
352 return null;
353 }
354
355
356
357
358
359
360
361
362 private DocumentBuilder initConstructor() throws MojoExecutionException
363 {
364 DocumentBuilder constructor = null;
365 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
366 try
367 {
368 constructor = factory.newDocumentBuilder();
369 }
370 catch ( ParserConfigurationException e )
371 {
372 getLog().error( "Unable to create a new xml document" );
373 throw new MojoExecutionException( "Cannot create the Document Builder : " + e.getMessage() );
374 }
375 return constructor;
376 }
377
378
379
380
381
382
383
384
385
386
387 private Document parseFile( File file, DocumentBuilder constructor ) throws MojoExecutionException
388 {
389 if ( constructor == null )
390 {
391 return null;
392 }
393
394 File targetFile = file.getAbsoluteFile();
395 getLog().info( "Parsing " + targetFile );
396 Document doc = null;
397 try
398 {
399 doc = constructor.parse( targetFile );
400 }
401 catch ( SAXException e )
402 {
403 getLog().error( "Cannot parse " + targetFile + " : " + e.getMessage() );
404 throw new MojoExecutionException( "Cannot parse " + targetFile + " : " + e.getMessage() );
405 }
406 catch ( IOException e )
407 {
408 getLog().error( "Cannot open " + targetFile + " : " + e.getMessage() );
409 throw new MojoExecutionException( "Cannot open " + targetFile + " : " + e.getMessage() );
410 }
411 return doc;
412 }
413
414
415
416
417
418
419
420
421
422 private void writeToFile( URI outputFilename, Node treeToBeWrite ) throws MojoExecutionException
423 {
424
425 Transformer transformer = null;
426 TransformerFactory tfabrique = TransformerFactory.newInstance();
427 try
428 {
429 transformer = tfabrique.newTransformer();
430 }
431 catch ( TransformerConfigurationException e )
432 {
433 getLog().error( "Unable to write to file: " + outputFilename.toString() );
434 throw new MojoExecutionException( "Unable to write to file: " + outputFilename.toString() + " : "
435 + e.getMessage() );
436 }
437 Properties proprietes = new Properties();
438 proprietes.put( "method", "xml" );
439 proprietes.put( "version", "1.0" );
440 proprietes.put( "encoding", "ISO-8859-1" );
441 proprietes.put( "standalone", "yes" );
442 proprietes.put( "indent", "yes" );
443 proprietes.put( "omit-xml-declaration", "no" );
444 transformer.setOutputProperties( proprietes );
445
446 DOMSource input = new DOMSource( treeToBeWrite );
447
448 File fichier = new File( outputFilename );
449 FileOutputStream flux = null;
450 try
451 {
452 flux = new FileOutputStream( fichier );
453 }
454 catch ( FileNotFoundException e )
455 {
456 getLog().error( "Unable to write to file: " + fichier.getName() );
457 throw new MojoExecutionException( "Unable to write to file: " + fichier.getName() + " : " + e.getMessage() );
458 }
459 Result output = new StreamResult( flux );
460 try
461 {
462 transformer.transform( input, output );
463 }
464 catch ( TransformerException e )
465 {
466 throw new MojoExecutionException( "Unable to write to file: " + outputFilename.toString() + " : "
467 + e.getMessage() );
468 }
469
470 try
471 {
472 flux.flush();
473 flux.close();
474 }
475 catch ( IOException e )
476 {
477 throw new MojoExecutionException( "IOException when closing file : " + e.getMessage() );
478 }
479 }
480 }