/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.felix.obrplugin; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Result; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.codehaus.plexus.util.FileUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * Clean an OBR repository by finding and removing missing resources. * * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ @Mojo( name = "clean", requiresProject = false, defaultPhase = LifecyclePhase.CLEAN ) public class ObrCleanRepo extends AbstractMojo { /** * OBR Repository. */ @Parameter( property = "obrRepository" ) private String obrRepository; /** * Local Repository. */ @Parameter( defaultValue = "${localRepository}", readonly = true, required = true ) private ArtifactRepository localRepository; public void execute() { if ( "NONE".equalsIgnoreCase( obrRepository ) || "false".equalsIgnoreCase( obrRepository ) ) { getLog().info( "Local OBR clean disabled (enable with -DobrRepository)" ); return; } try { // Compute local repository location URI repositoryXml = ObrUtils.findRepositoryXml( localRepository.getBasedir(), obrRepository ); if ( !"file".equals( repositoryXml.getScheme() ) ) { getLog().error( "The repository URI " + repositoryXml + " is not a local file" ); return; } File repositoryFile = new File( repositoryXml ); // Check if the file exist if ( !repositoryFile.exists() ) { getLog().error( "The repository file " + repositoryFile + " does not exist" ); return; } getLog().info( "Cleaning..." ); Document doc = parseFile( repositoryFile, initConstructor() ); Node finalDocument = cleanDocument( doc.getDocumentElement() ); // Analyze existing repository. if ( finalDocument == null ) { getLog().info( "Nothing to clean in " + repositoryFile ); } else { writeToFile( repositoryXml, finalDocument ); // Write the new file getLog().info( "Repository " + repositoryFile + " cleaned" ); } } catch ( Exception e ) { getLog().error( "Exception while cleaning local OBR: " + e.getLocalizedMessage(), e ); } } /** * Analyze the given XML tree (DOM of the repository file) and remove missing resources. * * @param elem : the input XML tree * @return the cleaned XML tree */ private Element cleanDocument( Element elem ) { String localRepoPath = localRepository.getBasedir(); URI baseURI = new File( localRepoPath + '/' ).toURI(); NodeList nodes = elem.getElementsByTagName( "resource" ); List toRemove = new ArrayList(); // First, look for missing resources for ( int i = 0; i < nodes.getLength(); i++ ) { Element n = ( Element ) nodes.item( i ); String value = n.getAttribute( "uri" ); URI resource; try { resource = baseURI.resolve( value ); } catch ( IllegalArgumentException e ) { getLog().error( "Malformed URL when creating the resource absolute URI : " + e.getMessage() ); return null; } if ( "file".equals( resource.getScheme() ) && !new File( resource ).exists() ) { getLog().info( "The bundle " + n.getAttribute( "presentationname" ) + " - " + n.getAttribute( "version" ) + " will be removed" ); toRemove.add( n ); } } Date d = new Date(); if ( toRemove.size() > 0 ) { // Then remove missing resources. for ( int i = 0; i < toRemove.size(); i++ ) { elem.removeChild( ( Node ) toRemove.get( i ) ); } // If we have to remove resources, we need to update 'lastmodified' attribute SimpleDateFormat format = new SimpleDateFormat( "yyyyMMddHHmmss.SSS" ); d.setTime( System.currentTimeMillis() ); elem.setAttribute( "lastmodified", format.format( d ) ); return elem; } return null; } /** * Initialize the document builder from Xerces. * * @return DocumentBuilder ready to create new document * @throws MojoExecutionException : occurs when the instantiation of the document builder fails */ private DocumentBuilder initConstructor() throws MojoExecutionException { DocumentBuilder constructor = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { constructor = factory.newDocumentBuilder(); } catch ( ParserConfigurationException e ) { getLog().error( "Unable to create a new xml document" ); throw new MojoExecutionException( "Cannot create the Document Builder : " + e.getMessage() ); } return constructor; } /** * Open an XML file. * * @param file : XML file path * @param constructor DocumentBuilder get from xerces * @return Document which describes this file * @throws MojoExecutionException occurs when the given file cannot be opened or is a valid XML file. */ private Document parseFile( File file, DocumentBuilder constructor ) throws MojoExecutionException { if ( constructor == null ) { return null; } // The document is the root of the DOM tree. File targetFile = file.getAbsoluteFile(); getLog().info( "Parsing " + targetFile ); Document doc = null; try { doc = constructor.parse( targetFile ); } catch ( SAXException e ) { getLog().error( "Cannot parse " + targetFile + " : " + e.getMessage() ); throw new MojoExecutionException( "Cannot parse " + targetFile + " : " + e.getMessage() ); } catch ( IOException e ) { getLog().error( "Cannot open " + targetFile + " : " + e.getMessage() ); throw new MojoExecutionException( "Cannot open " + targetFile + " : " + e.getMessage() ); } return doc; } /** * write a Node in a xml file. * * @param outputFilename URI to the output file * @param treeToBeWrite Node root of the tree to be write in file * @throws MojoExecutionException if the plugin failed */ private void writeToFile( URI outputFilename, Node treeToBeWrite ) throws MojoExecutionException { // init the transformer Transformer transformer = null; TransformerFactory tfabrique = TransformerFactory.newInstance(); try { transformer = tfabrique.newTransformer(); } catch ( TransformerConfigurationException e ) { getLog().error( "Unable to write to file: " + outputFilename.toString() ); throw new MojoExecutionException( "Unable to write to file: " + outputFilename.toString() + " : " + e.getMessage() ); } Properties proprietes = new Properties(); proprietes.put( "method", "xml" ); proprietes.put( "version", "1.0" ); proprietes.put( "encoding", "ISO-8859-1" ); proprietes.put( "standalone", "yes" ); proprietes.put( "indent", "yes" ); proprietes.put( "omit-xml-declaration", "no" ); transformer.setOutputProperties( proprietes ); DOMSource input = new DOMSource( treeToBeWrite ); File fichier = null; FileOutputStream flux = null; try { fichier = File.createTempFile( "repository", ".xml" ); flux = new FileOutputStream( fichier ); } catch ( IOException e ) { getLog().error( "Unable to write to file: " + fichier.getName() ); throw new MojoExecutionException( "Unable to write to file: " + fichier.getName() + " : " + e.getMessage() ); } Result output = new StreamResult( flux ); try { transformer.transform( input, output ); } catch ( TransformerException e ) { throw new MojoExecutionException( "Unable to write to file: " + outputFilename.toString() + " : " + e.getMessage() ); } try { flux.flush(); flux.close(); FileUtils.rename( fichier, new File( outputFilename ) ); } catch ( IOException e ) { throw new MojoExecutionException( "IOException when closing file : " + e.getMessage() ); } } }