/* | |
* 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.sandbox.obr.plugin; | |
import java.io.File; | |
import java.io.FileNotFoundException; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.net.URI; | |
import java.text.SimpleDateFormat; | |
import java.util.Date; | |
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.plugin.MojoExecutionException; | |
import org.apache.maven.plugin.logging.Log; | |
import org.apache.maven.project.MavenProject; | |
import org.w3c.dom.Document; | |
import org.w3c.dom.Element; | |
import org.w3c.dom.NamedNodeMap; | |
import org.w3c.dom.Node; | |
import org.w3c.dom.NodeList; | |
import org.xml.sax.SAXException; | |
/** | |
* this class parse the old repository.xml file build the bundle resource description and update the repository. | |
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> | |
*/ | |
public class ObrUpdate { | |
/** | |
* generate the date format to insert it in repository descriptor file. | |
*/ | |
static SimpleDateFormat m_format = new SimpleDateFormat("yyyyMMddHHmmss.SSS"); | |
/** | |
* logger for this plugin. | |
*/ | |
private Log m_logger; | |
/** | |
* name and path to the repository descriptor file. | |
*/ | |
private URI m_repoFilename; | |
/** | |
* name and path to the obr.xml file. | |
*/ | |
private String m_obrXml; | |
/** | |
* name and path to the bundle jar file. | |
*/ | |
private String m_outputFile; | |
/** | |
* maven project description. | |
*/ | |
private MavenProject m_project; | |
/** | |
* user configuration information. | |
*/ | |
private Config m_userConfig; | |
/** | |
* used to build another xml document. | |
*/ | |
private DocumentBuilder m_constructor; | |
/** | |
* root on parent document. | |
*/ | |
private Document m_repoDoc; | |
/** | |
* used to determine the first free id. | |
*/ | |
private boolean[] m_idTab; | |
/** | |
* first Node on repository descriptor tree. | |
*/ | |
private Node m_root; | |
/** | |
* used to extract bindex bundle information. | |
*/ | |
private ExtractBindexInfo m_ebi; | |
/** | |
* used to store bundle information. | |
*/ | |
private ResourcesBundle m_resourceBundle; | |
/** | |
* pathfile to the repository descriptor file. | |
*/ | |
private PathFile m_repo; | |
/** | |
* initialize information. | |
* @param repoFilename path to the repository descriptor file | |
* @param obrXml path and filename to the obr.xml file | |
* @param project maven project description | |
* @param outputFile path to the bundle jar file | |
* @param localRepo only path to the local repository | |
* @param userConfig user information | |
* @param log plugin logger | |
*/ | |
public ObrUpdate(PathFile repoFilename, String obrXml, MavenProject project, String outputFile, String localRepo, Config userConfig, Log log) { | |
// this.m_localRepo = localRepo; | |
this.m_outputFile = outputFile; | |
this.m_repoFilename = repoFilename.getUri(); | |
this.m_obrXml = obrXml; | |
this.m_project = project; | |
this.m_logger = log; | |
this.m_userConfig = userConfig; | |
// init the tab | |
m_idTab = new boolean[0]; | |
m_resourceBundle = new ResourcesBundle(log); | |
if (userConfig.isRemotely()) { | |
this.m_repo = new PathFile(localRepo); | |
} else { | |
this.m_repo = repoFilename; | |
} | |
// System.err.println("Construct: "+repoFilename.getAbsoluteFilename()); | |
} | |
/** | |
* update the repository descriptor file. parse the old repository descriptor file, get the old reference of the bundle or determine the id for a new bundle, extract information from bindex set the new information in descriptor file and save it. | |
* @throws MojoExecutionException if the plugin failed | |
*/ | |
public void updateRepository() throws MojoExecutionException { | |
m_logger.debug(" (f) m_obrXml = " + m_obrXml); | |
m_logger.debug(" (f) m_outputFile = " + m_outputFile); | |
m_logger.debug(" (f) m_repoFilename = " + m_repoFilename); | |
m_constructor = initConstructor(); | |
if (m_constructor == null) { | |
return; | |
} | |
// get the file size | |
PathFile pf = new PathFile(m_outputFile); | |
File fout = pf.getFile(); | |
pf.setBaseDir(m_repo.getOnlyAbsolutePath()); | |
if (fout.exists()) { | |
m_resourceBundle.setSize(String.valueOf(fout.length())); | |
if (m_userConfig.isPathRelative()) { | |
m_resourceBundle.setUri(pf.getOnlyRelativeFilename().replace('\\', '/')); | |
} else { | |
m_resourceBundle.setUri("file:" + m_outputFile); | |
} | |
} else { | |
m_logger.error("file doesn't exist: " + m_outputFile); | |
return; | |
} | |
// parse repository | |
if (parseRepositoryXml() == -1) { return; } | |
// parse the obr.xml file | |
if (m_obrXml != null) { | |
// URL url = getClass().getResource("/SchemaObr.xsd"); | |
// TODO validate obr.xml file | |
Document obrXmlDoc = parseFile(m_obrXml, m_constructor); | |
if (obrXmlDoc == null) { return; } | |
Node obrXmlRoot = (Element) obrXmlDoc.getDocumentElement(); | |
// sort the obr file | |
sortObrXml(obrXmlRoot); | |
} | |
// use bindex to extract bundle information | |
try { | |
m_ebi = new ExtractBindexInfo(m_repoFilename, m_outputFile); | |
} catch (MojoExecutionException e) { | |
m_logger.error("unable to build Bindex informations"); | |
e.printStackTrace(); | |
throw new MojoExecutionException("MojoFailureException"); | |
} | |
m_resourceBundle.construct(m_project, m_ebi); | |
if (!walkOnTree(m_root)) { | |
// the correct resource node was not found, we must create it | |
// we calcul the new id | |
int id = -1; | |
for (int i = 1; i < m_idTab.length; i++) { | |
if (!m_idTab[i]) { | |
id = i; | |
break; | |
} | |
} | |
if (id == -1) { id = m_idTab.length; } | |
searchRepository(m_root, id); | |
} | |
// the repository.xml file have been modified, so we save it | |
writeToFile(m_repoFilename, m_root); | |
} | |
/** | |
* init the document builder from xerces. | |
* @return DocumentBuilder ready to create new document | |
*/ | |
private DocumentBuilder initConstructor() { | |
DocumentBuilder constructor = null; | |
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | |
try { | |
constructor = factory.newDocumentBuilder(); | |
} catch (ParserConfigurationException e) { | |
m_logger.error("unable to create a new xml document"); | |
e.printStackTrace(); | |
} | |
return constructor; | |
} | |
/** | |
* Parse the reporitory descriptor file. | |
* | |
* @return 0 if the bundle is already in the descriptor, else -1 | |
* @throws MojoExecutionException if the plugin failed | |
*/ | |
private int parseRepositoryXml() throws MojoExecutionException { | |
File fout = new File(m_repoFilename); | |
if (!fout.exists()) { | |
// create the repository.xml | |
try { | |
fout.createNewFile(); | |
m_logger.info("Created new repository.xml file in "+fout.getAbsolutePath()); | |
} catch (IOException e) { | |
m_logger.error("Cannot create file " + fout.getAbsolutePath()); | |
e.printStackTrace(); | |
return -1; | |
} | |
Document doc = m_constructor.newDocument(); | |
// create xml tree | |
Date d = new Date(); | |
d.setTime(System.currentTimeMillis()); | |
Element root = doc.createElement("repository"); | |
root.setAttribute("lastmodified", m_format.format(d)); | |
root.setAttribute("name", "MyRepository"); | |
try { | |
writeToFile(m_repoFilename, root); | |
} catch (MojoExecutionException e) { | |
e.printStackTrace(); | |
throw new MojoExecutionException("MojoExecutionException"); | |
} | |
} | |
// now we parse the repository.xml file | |
m_repoDoc = parseFile(fout.getAbsolutePath(), m_constructor); | |
if (m_repoDoc == null) { return -1; } | |
m_root = (Element) m_repoDoc.getDocumentElement(); | |
return 0; | |
} | |
/** | |
* transform a xml file to a xerces Document. | |
* @param filename path to the xml file | |
* @param constructor DocumentBuilder get from xerces | |
* @return Document which describe this file | |
*/ | |
private Document parseFile(String filename, DocumentBuilder constructor) { | |
if (constructor == null) { return null; } | |
// The document is the root of the DOM tree. | |
m_logger.info("Try to open: " + filename); | |
Document doc = null; | |
try { | |
doc = constructor.parse(new File(filename)); | |
} catch (SAXException e) { | |
e.printStackTrace(); | |
return null; | |
} catch (IOException e) { | |
m_logger.error("cannot open file: " + filename); | |
e.printStackTrace(); | |
return null; | |
} | |
return doc; | |
} | |
/** | |
* put the information from obr.xml into ressourceBundle object. | |
* @param node Node to the OBR.xml file | |
*/ | |
private void sortObrXml(Node node) { | |
if (node.getNodeName().compareTo("require") == 0) { | |
Require newRequireNode = new Require(); | |
NamedNodeMap list = node.getAttributes(); | |
try { | |
newRequireNode.setExtend(list.getNamedItem("extend").getNodeValue()); | |
newRequireNode.setMultiple(list.getNamedItem("multiple").getNodeValue()); | |
newRequireNode.setOptional(list.getNamedItem("optional").getNodeValue()); | |
newRequireNode.setFilter(list.getNamedItem("filter").getNodeValue()); | |
newRequireNode.setName(list.getNamedItem("name").getNodeValue()); | |
} catch (NullPointerException e) { | |
m_logger.error("the obr.xml file seems to be invalid in a \"require\" tag (one or more attributes are missing)"); | |
// e.printStackTrace(); | |
} | |
newRequireNode.setValue(node.getTextContent()); | |
m_resourceBundle.addRequire(newRequireNode); | |
} else if (node.getNodeName().compareTo("capability") == 0) { | |
Capability newCapability = new Capability(); | |
try { | |
newCapability.setName(node.getAttributes().getNamedItem("name").getNodeValue()); | |
} catch (NullPointerException e) { | |
m_logger.error("attribute \"name\" is missing in obr.xml in a \"capability\" tag"); | |
e.printStackTrace(); | |
} | |
NodeList list = node.getChildNodes(); | |
for (int i = 0; i < list.getLength(); i++) { | |
PElement p = new PElement(); | |
Node n = list.item(i); | |
Node item = null; | |
// System.err.println(n.getNodeName()); | |
if (n.getNodeName().compareTo("p") == 0) { | |
p.setN(n.getAttributes().getNamedItem("n").getNodeValue()); | |
item = n.getAttributes().getNamedItem("t"); | |
if (item != null) { p.setT(item.getNodeValue()); } | |
item = n.getAttributes().getNamedItem("v"); | |
if (item != null) { p.setV(item.getNodeValue()); } | |
newCapability.addP(p); | |
} | |
} | |
m_resourceBundle.addCapability(newCapability); | |
} else if (node.getNodeName().compareTo("category") == 0) { | |
Category newCategory = new Category(); | |
newCategory.setId(node.getAttributes().getNamedItem("id").getNodeValue()); | |
m_resourceBundle.addCategory(newCategory); | |
} else { | |
NodeList list = node.getChildNodes(); | |
for (int i = 0; i < list.getLength(); i++) { | |
sortObrXml(list.item(i)); | |
} | |
} | |
} | |
/** | |
* 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) { | |
m_logger.error("Unable to write to file: " + outputFilename.toString()); | |
e.printStackTrace(); | |
throw new MojoExecutionException("TransformerConfigurationException"); | |
} | |
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 = new File(outputFilename); | |
FileOutputStream flux = null; | |
try { | |
flux = new FileOutputStream(fichier); | |
} catch (FileNotFoundException e) { | |
m_logger.error("Unable to write to file: " + fichier.getName()); | |
e.printStackTrace(); | |
throw new MojoExecutionException("FileNotFoundException"); | |
} | |
Result output = new StreamResult(flux); | |
try { | |
transformer.transform(input, output); | |
} catch (TransformerException e) { | |
e.printStackTrace(); | |
throw new MojoExecutionException("TransformerException"); | |
} | |
try { | |
flux.flush(); | |
flux.close(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
throw new MojoExecutionException("IOException"); | |
} | |
} | |
/** | |
* walk on the tree until the targeted node was found. | |
* @param node targeted node | |
* @return true if the requiered node was found else false. | |
*/ | |
private boolean walkOnTree(Node node) { | |
if (node.getNodeName().compareTo("resource") == 0) { | |
return resource(node); | |
} else { // look at the repository node (first in the file) | |
if (node.getNodeName().compareTo("repository") == 0) { | |
Date d = new Date(); | |
d.setTime(System.currentTimeMillis()); | |
NamedNodeMap nList = node.getAttributes(); | |
Node n = nList.getNamedItem("lastmodified"); | |
n.setNodeValue(m_format.format(d)); | |
} | |
NodeList list = node.getChildNodes(); | |
if (list.getLength() > 0) { | |
for (int i = 0; i < list.getLength(); i++) { | |
if (walkOnTree(list.item(i))) { return true; } | |
} | |
} | |
return false; | |
} | |
} | |
/** | |
* put the resource bundle in the tree. | |
* @param node Node on the xml file | |
* @param id id of the bundle ressource | |
*/ | |
private void searchRepository(Node node, int id) { | |
if (node.getNodeName().compareTo("repository") == 0) { | |
m_resourceBundle.setId(String.valueOf(id)); | |
node.appendChild(m_resourceBundle.getNode(m_repoDoc)); | |
return; | |
} else { | |
NodeList list = node.getChildNodes(); | |
if (list.getLength() > 0) { | |
for (int i = 0; i < list.getLength(); i++) { | |
searchRepository(list.item(i), id); | |
} | |
} | |
} | |
} | |
/** | |
* compare two node and update the array which compute the smallest free id. | |
* @param node : node | |
* @return true if the node is the same bundle than the ressourceBundle, else false. | |
*/ | |
private boolean resource(Node node) { | |
// this part save all the id free if we need to add resource | |
int id = Integer.parseInt(node.getAttributes().getNamedItem("id").getNodeValue()); | |
if (id >= m_idTab.length) { | |
// resize tab | |
boolean[] bt = new boolean[id + 1]; | |
for (int i = 0; i < id + 1; i++) { | |
if (m_idTab.length > i && m_idTab[i]) { | |
bt[i] = true; | |
} else { | |
bt[i] = false; | |
} | |
} | |
m_idTab = bt; | |
} | |
m_idTab[id] = true; | |
NamedNodeMap map = node.getAttributes(); | |
if (m_resourceBundle.isSameBundleResource(map.getNamedItem("symbolicname").getNodeValue(), map.getNamedItem("presentationname").getNodeValue(), map.getNamedItem("version").getNodeValue())) { | |
m_resourceBundle.setId(String.valueOf(id)); | |
node.getParentNode().replaceChild(m_resourceBundle.getNode(m_repoDoc), node); | |
return true; | |
} | |
return false; | |
} | |
} |