Stefano Lenzi | 476013d | 2007-09-21 23:59:54 +0000 | [diff] [blame] | 1 | /*
|
| 2 | * Licensed to the Apache Software Foundation (ASF) under one
|
| 3 | * or more contributor license agreements. See the NOTICE file
|
| 4 | * distributed with this work for additional information
|
| 5 | * regarding copyright ownership. The ASF licenses this file
|
| 6 | * to you under the Apache License, Version 2.0 (the
|
| 7 | * "License"); you may not use this file except in compliance
|
| 8 | * with the License. You may obtain a copy of the License at
|
| 9 | *
|
| 10 | * http://www.apache.org/licenses/LICENSE-2.0
|
| 11 | *
|
| 12 | * Unless required by applicable law or agreed to in writing,
|
| 13 | * software distributed under the License is distributed on an
|
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
| 15 | * KIND, either express or implied. See the License for the
|
| 16 | * specific language governing permissions and limitations
|
| 17 | * under the License.
|
| 18 | */
|
| 19 | package org.apache.felix.sandbox.obr.plugin;
|
| 20 |
|
| 21 | import java.io.File;
|
| 22 | import java.io.FileNotFoundException;
|
| 23 | import java.io.FileOutputStream;
|
| 24 | import java.io.IOException;
|
| 25 | import java.net.URI;
|
| 26 | import java.text.SimpleDateFormat;
|
| 27 | import java.util.Date;
|
| 28 | import java.util.Properties;
|
| 29 |
|
| 30 | import javax.xml.parsers.DocumentBuilder;
|
| 31 | import javax.xml.parsers.DocumentBuilderFactory;
|
| 32 | import javax.xml.parsers.ParserConfigurationException;
|
| 33 | import javax.xml.transform.Result;
|
| 34 | import javax.xml.transform.Transformer;
|
| 35 | import javax.xml.transform.TransformerConfigurationException;
|
| 36 | import javax.xml.transform.TransformerException;
|
| 37 | import javax.xml.transform.TransformerFactory;
|
| 38 | import javax.xml.transform.dom.DOMSource;
|
| 39 | import javax.xml.transform.stream.StreamResult;
|
| 40 |
|
| 41 | import org.apache.maven.plugin.MojoExecutionException;
|
| 42 | import org.apache.maven.plugin.logging.Log;
|
| 43 | import org.apache.maven.project.MavenProject;
|
| 44 | import org.w3c.dom.Document;
|
| 45 | import org.w3c.dom.Element;
|
| 46 | import org.w3c.dom.NamedNodeMap;
|
| 47 | import org.w3c.dom.Node;
|
| 48 | import org.w3c.dom.NodeList;
|
| 49 | import org.xml.sax.SAXException;
|
| 50 |
|
| 51 | /**
|
| 52 | * this class parse the old repository.xml file build the bundle resource description and update the repository.
|
| 53 | * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
|
| 54 | */
|
| 55 |
|
| 56 | public class ObrUpdate {
|
| 57 | /**
|
| 58 | * generate the date format to insert it in repository descriptor file.
|
| 59 | */
|
| 60 | static SimpleDateFormat m_format = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
|
| 61 |
|
| 62 | /**
|
| 63 | * logger for this plugin.
|
| 64 | */
|
| 65 | private Log m_logger;
|
| 66 |
|
| 67 | /**
|
| 68 | * name and path to the repository descriptor file.
|
| 69 | */
|
| 70 | private URI m_repoFilename;
|
| 71 |
|
| 72 | /**
|
| 73 | * name and path to the obr.xml file.
|
| 74 | */
|
| 75 | private String m_obrXml;
|
| 76 |
|
| 77 | /**
|
| 78 | * name and path to the bundle jar file.
|
| 79 | */
|
| 80 | private String m_outputFile;
|
| 81 |
|
| 82 | /**
|
| 83 | * maven project description.
|
| 84 | */
|
| 85 | private MavenProject m_project;
|
| 86 |
|
| 87 | /**
|
| 88 | * user configuration information.
|
| 89 | */
|
| 90 | private Config m_userConfig;
|
| 91 |
|
| 92 | /**
|
| 93 | * used to build another xml document.
|
| 94 | */
|
| 95 | private DocumentBuilder m_constructor;
|
| 96 |
|
| 97 | /**
|
| 98 | * root on parent document.
|
| 99 | */
|
| 100 | private Document m_repoDoc;
|
| 101 |
|
| 102 | /**
|
| 103 | * used to determine the first free id.
|
| 104 | */
|
| 105 | private boolean[] m_idTab;
|
| 106 |
|
| 107 | /**
|
| 108 | * first Node on repository descriptor tree.
|
| 109 | */
|
| 110 | private Node m_root;
|
| 111 |
|
| 112 | /**
|
| 113 | * used to extract bindex bundle information.
|
| 114 | */
|
| 115 | private ExtractBindexInfo m_ebi;
|
| 116 |
|
| 117 | /**
|
| 118 | * used to store bundle information.
|
| 119 | */
|
| 120 | private ResourcesBundle m_resourceBundle;
|
| 121 |
|
| 122 | /**
|
| 123 | * pathfile to the repository descriptor file.
|
| 124 | */
|
| 125 | private PathFile m_repo;
|
| 126 |
|
| 127 | /**
|
| 128 | * initialize information.
|
| 129 | * @param repoFilename path to the repository descriptor file
|
| 130 | * @param obrXml path and filename to the obr.xml file
|
| 131 | * @param project maven project description
|
| 132 | * @param outputFile path to the bundle jar file
|
| 133 | * @param localRepo only path to the local repository
|
| 134 | * @param userConfig user information
|
| 135 | * @param log plugin logger
|
| 136 | */
|
| 137 | public ObrUpdate(PathFile repoFilename, String obrXml, MavenProject project, String outputFile, String localRepo, Config userConfig, Log log) {
|
| 138 | // this.m_localRepo = localRepo;
|
| 139 | this.m_outputFile = outputFile;
|
| 140 | this.m_repoFilename = repoFilename.getUri();
|
| 141 | this.m_obrXml = obrXml;
|
| 142 | this.m_project = project;
|
| 143 | this.m_logger = log;
|
| 144 |
|
| 145 | this.m_userConfig = userConfig;
|
| 146 | // init the tab
|
| 147 | m_idTab = new boolean[0];
|
| 148 |
|
| 149 | m_resourceBundle = new ResourcesBundle(log);
|
| 150 |
|
| 151 | if (userConfig.isRemotely()) {
|
| 152 | this.m_repo = new PathFile(localRepo);
|
| 153 | } else {
|
| 154 | this.m_repo = repoFilename;
|
| 155 | }
|
| 156 | // System.err.println("Construct: "+repoFilename.getAbsoluteFilename());
|
| 157 | }
|
| 158 |
|
| 159 | /**
|
| 160 | * 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.
|
| 161 | * @throws MojoExecutionException if the plugin failed
|
| 162 | */
|
| 163 | public void updateRepository() throws MojoExecutionException {
|
| 164 |
|
| 165 | m_logger.debug(" (f) m_obrXml = " + m_obrXml);
|
| 166 | m_logger.debug(" (f) m_outputFile = " + m_outputFile);
|
| 167 | m_logger.debug(" (f) m_repoFilename = " + m_repoFilename);
|
| 168 |
|
| 169 | m_constructor = initConstructor();
|
| 170 |
|
| 171 | if (m_constructor == null) {
|
| 172 | return;
|
| 173 | }
|
| 174 |
|
| 175 | // get the file size
|
| 176 | PathFile pf = new PathFile(m_outputFile);
|
| 177 | File fout = pf.getFile();
|
| 178 | pf.setBaseDir(m_repo.getOnlyAbsolutePath());
|
| 179 | if (fout.exists()) {
|
| 180 | m_resourceBundle.setSize(String.valueOf(fout.length()));
|
| 181 | if (m_userConfig.isPathRelative()) {
|
| 182 | m_resourceBundle.setUri(pf.getOnlyRelativeFilename().replace('\\', '/'));
|
| 183 | } else {
|
| 184 | m_resourceBundle.setUri("file:" + m_outputFile);
|
| 185 | }
|
| 186 | } else {
|
| 187 | m_logger.error("file doesn't exist: " + m_outputFile);
|
| 188 | return;
|
| 189 | }
|
| 190 |
|
| 191 | // parse repository
|
| 192 | if (parseRepositoryXml() == -1) { return; }
|
| 193 |
|
| 194 | // parse the obr.xml file
|
| 195 | if (m_obrXml != null) {
|
| 196 | // URL url = getClass().getResource("/SchemaObr.xsd");
|
| 197 | // TODO validate obr.xml file
|
| 198 |
|
| 199 | Document obrXmlDoc = parseFile(m_obrXml, m_constructor);
|
| 200 | if (obrXmlDoc == null) { return; }
|
| 201 | Node obrXmlRoot = (Element) obrXmlDoc.getDocumentElement();
|
| 202 | // sort the obr file
|
| 203 | sortObrXml(obrXmlRoot);
|
| 204 | }
|
| 205 |
|
| 206 | // use bindex to extract bundle information
|
| 207 | try {
|
| 208 | m_ebi = new ExtractBindexInfo(m_repoFilename, m_outputFile);
|
| 209 | } catch (MojoExecutionException e) {
|
| 210 | m_logger.error("unable to build Bindex informations");
|
| 211 | e.printStackTrace();
|
| 212 | throw new MojoExecutionException("MojoFailureException");
|
| 213 | }
|
| 214 |
|
| 215 | m_resourceBundle.construct(m_project, m_ebi);
|
| 216 |
|
| 217 | if (!walkOnTree(m_root)) {
|
| 218 | // the correct resource node was not found, we must create it
|
| 219 | // we calcul the new id
|
| 220 | int id = -1;
|
| 221 | for (int i = 1; i < m_idTab.length; i++) {
|
| 222 | if (!m_idTab[i]) {
|
| 223 | id = i;
|
| 224 | break;
|
| 225 | }
|
| 226 | }
|
| 227 | if (id == -1) { id = m_idTab.length; }
|
| 228 |
|
| 229 | searchRepository(m_root, id);
|
| 230 | }
|
| 231 |
|
| 232 | // the repository.xml file have been modified, so we save it
|
| 233 | writeToFile(m_repoFilename, m_root);
|
| 234 | }
|
| 235 |
|
| 236 | /**
|
| 237 | * init the document builder from xerces.
|
| 238 | * @return DocumentBuilder ready to create new document
|
| 239 | */
|
| 240 | private DocumentBuilder initConstructor() {
|
| 241 | DocumentBuilder constructor = null;
|
| 242 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
| 243 | try {
|
| 244 | constructor = factory.newDocumentBuilder();
|
| 245 | } catch (ParserConfigurationException e) {
|
| 246 | m_logger.error("unable to create a new xml document");
|
| 247 | e.printStackTrace();
|
| 248 | }
|
| 249 | return constructor;
|
| 250 |
|
| 251 | }
|
| 252 |
|
| 253 | /**
|
| 254 | * Parse the reporitory descriptor file.
|
| 255 | *
|
| 256 | * @return 0 if the bundle is already in the descriptor, else -1
|
| 257 | * @throws MojoExecutionException if the plugin failed
|
| 258 | */
|
| 259 | private int parseRepositoryXml() throws MojoExecutionException {
|
| 260 |
|
| 261 | File fout = new File(m_repoFilename);
|
| 262 | if (!fout.exists()) {
|
| 263 | // create the repository.xml
|
| 264 | try {
|
| 265 |
|
| 266 | fout.createNewFile();
|
| 267 | m_logger.info("Created new repository.xml file in "+fout.getAbsolutePath());
|
| 268 | } catch (IOException e) {
|
| 269 | m_logger.error("Cannot create file " + fout.getAbsolutePath());
|
| 270 | e.printStackTrace();
|
| 271 | return -1;
|
| 272 | }
|
| 273 |
|
| 274 | Document doc = m_constructor.newDocument();
|
| 275 | // create xml tree
|
| 276 | Date d = new Date();
|
| 277 | d.setTime(System.currentTimeMillis());
|
| 278 | Element root = doc.createElement("repository");
|
| 279 | root.setAttribute("lastmodified", m_format.format(d));
|
| 280 | root.setAttribute("name", "MyRepository");
|
| 281 | try {
|
| 282 | writeToFile(m_repoFilename, root);
|
| 283 | } catch (MojoExecutionException e) {
|
| 284 | e.printStackTrace();
|
| 285 | throw new MojoExecutionException("MojoExecutionException");
|
| 286 | }
|
| 287 | }
|
| 288 |
|
| 289 | // now we parse the repository.xml file
|
| 290 | m_repoDoc = parseFile(fout.getAbsolutePath(), m_constructor);
|
| 291 | if (m_repoDoc == null) { return -1; }
|
| 292 |
|
| 293 | m_root = (Element) m_repoDoc.getDocumentElement();
|
| 294 | return 0;
|
| 295 |
|
| 296 | }
|
| 297 |
|
| 298 | /**
|
| 299 | * transform a xml file to a xerces Document.
|
| 300 | * @param filename path to the xml file
|
| 301 | * @param constructor DocumentBuilder get from xerces
|
| 302 | * @return Document which describe this file
|
| 303 | */
|
| 304 | private Document parseFile(String filename, DocumentBuilder constructor) {
|
| 305 | if (constructor == null) { return null; }
|
| 306 | // The document is the root of the DOM tree.
|
| 307 | m_logger.info("Try to open: " + filename);
|
| 308 | Document doc = null;
|
| 309 | try {
|
| 310 | doc = constructor.parse(new File(filename));
|
| 311 | } catch (SAXException e) {
|
| 312 | e.printStackTrace();
|
| 313 | return null;
|
| 314 | } catch (IOException e) {
|
| 315 | m_logger.error("cannot open file: " + filename);
|
| 316 | e.printStackTrace();
|
| 317 | return null;
|
| 318 | }
|
| 319 | return doc;
|
| 320 | }
|
| 321 |
|
| 322 | /**
|
| 323 | * put the information from obr.xml into ressourceBundle object.
|
| 324 | * @param node Node to the OBR.xml file
|
| 325 | */
|
| 326 | private void sortObrXml(Node node) {
|
| 327 | if (node.getNodeName().compareTo("require") == 0) {
|
| 328 | Require newRequireNode = new Require();
|
| 329 | NamedNodeMap list = node.getAttributes();
|
| 330 | try {
|
| 331 | newRequireNode.setExtend(list.getNamedItem("extend").getNodeValue());
|
| 332 | newRequireNode.setMultiple(list.getNamedItem("multiple").getNodeValue());
|
| 333 | newRequireNode.setOptional(list.getNamedItem("optional").getNodeValue());
|
| 334 | newRequireNode.setFilter(list.getNamedItem("filter").getNodeValue());
|
| 335 | newRequireNode.setName(list.getNamedItem("name").getNodeValue());
|
| 336 | } catch (NullPointerException e) {
|
| 337 | m_logger.error("the obr.xml file seems to be invalid in a \"require\" tag (one or more attributes are missing)");
|
| 338 | // e.printStackTrace();
|
| 339 | }
|
Stuart McCulloch | bfc05cf | 2007-09-26 15:34:00 +0000 | [diff] [blame^] | 340 | newRequireNode.setValue(XmlHelper.getTextContent(node));
|
Stefano Lenzi | 476013d | 2007-09-21 23:59:54 +0000 | [diff] [blame] | 341 | m_resourceBundle.addRequire(newRequireNode);
|
| 342 | } else if (node.getNodeName().compareTo("capability") == 0) {
|
| 343 | Capability newCapability = new Capability();
|
| 344 | try {
|
| 345 | newCapability.setName(node.getAttributes().getNamedItem("name").getNodeValue());
|
| 346 | } catch (NullPointerException e) {
|
| 347 | m_logger.error("attribute \"name\" is missing in obr.xml in a \"capability\" tag");
|
| 348 | e.printStackTrace();
|
| 349 | }
|
| 350 | NodeList list = node.getChildNodes();
|
| 351 | for (int i = 0; i < list.getLength(); i++) {
|
| 352 | PElement p = new PElement();
|
| 353 | Node n = list.item(i);
|
| 354 | Node item = null;
|
| 355 | // System.err.println(n.getNodeName());
|
| 356 | if (n.getNodeName().compareTo("p") == 0) {
|
| 357 |
|
| 358 | p.setN(n.getAttributes().getNamedItem("n").getNodeValue());
|
| 359 | item = n.getAttributes().getNamedItem("t");
|
| 360 | if (item != null) { p.setT(item.getNodeValue()); }
|
| 361 | item = n.getAttributes().getNamedItem("v");
|
| 362 | if (item != null) { p.setV(item.getNodeValue()); }
|
| 363 |
|
| 364 | newCapability.addP(p);
|
| 365 | }
|
| 366 | }
|
| 367 | m_resourceBundle.addCapability(newCapability);
|
| 368 | } else if (node.getNodeName().compareTo("category") == 0) {
|
| 369 | Category newCategory = new Category();
|
| 370 | newCategory.setId(node.getAttributes().getNamedItem("id").getNodeValue());
|
| 371 | m_resourceBundle.addCategory(newCategory);
|
| 372 | } else {
|
| 373 | NodeList list = node.getChildNodes();
|
| 374 | for (int i = 0; i < list.getLength(); i++) {
|
| 375 | sortObrXml(list.item(i));
|
| 376 | }
|
| 377 | }
|
| 378 | }
|
| 379 |
|
| 380 | /**
|
| 381 | * write a Node in a xml file.
|
| 382 | * @param outputFilename URI to the output file
|
| 383 | * @param treeToBeWrite Node root of the tree to be write in file
|
| 384 | * @throws MojoExecutionException if the plugin failed
|
| 385 | */
|
| 386 | private void writeToFile(URI outputFilename, Node treeToBeWrite) throws MojoExecutionException {
|
| 387 | // init the transformer
|
| 388 | Transformer transformer = null;
|
| 389 | TransformerFactory tfabrique = TransformerFactory.newInstance();
|
| 390 | try {
|
| 391 | transformer = tfabrique.newTransformer();
|
| 392 | } catch (TransformerConfigurationException e) {
|
| 393 | m_logger.error("Unable to write to file: " + outputFilename.toString());
|
| 394 | e.printStackTrace();
|
| 395 | throw new MojoExecutionException("TransformerConfigurationException");
|
| 396 | }
|
| 397 | Properties proprietes = new Properties();
|
| 398 | proprietes.put("method", "xml");
|
| 399 | proprietes.put("version", "1.0");
|
| 400 | proprietes.put("encoding", "ISO-8859-1");
|
| 401 | proprietes.put("standalone", "yes");
|
| 402 | proprietes.put("indent", "yes");
|
| 403 | proprietes.put("omit-xml-declaration", "no");
|
| 404 | transformer.setOutputProperties(proprietes);
|
| 405 |
|
| 406 | DOMSource input = new DOMSource(treeToBeWrite);
|
| 407 |
|
| 408 | File fichier = new File(outputFilename);
|
| 409 | FileOutputStream flux = null;
|
| 410 | try {
|
| 411 | flux = new FileOutputStream(fichier);
|
| 412 | } catch (FileNotFoundException e) {
|
| 413 | m_logger.error("Unable to write to file: " + fichier.getName());
|
| 414 | e.printStackTrace();
|
| 415 | throw new MojoExecutionException("FileNotFoundException");
|
| 416 | }
|
| 417 | Result output = new StreamResult(flux);
|
| 418 | try {
|
| 419 | transformer.transform(input, output);
|
| 420 | } catch (TransformerException e) {
|
| 421 | e.printStackTrace();
|
| 422 | throw new MojoExecutionException("TransformerException");
|
| 423 | }
|
| 424 |
|
| 425 | try {
|
| 426 | flux.flush();
|
| 427 | flux.close();
|
| 428 | } catch (IOException e) {
|
| 429 | e.printStackTrace();
|
| 430 | throw new MojoExecutionException("IOException");
|
| 431 | }
|
| 432 |
|
| 433 | }
|
| 434 |
|
| 435 | /**
|
| 436 | * walk on the tree until the targeted node was found.
|
| 437 | * @param node targeted node
|
| 438 | * @return true if the requiered node was found else false.
|
| 439 | */
|
| 440 | private boolean walkOnTree(Node node) {
|
| 441 |
|
| 442 | if (node.getNodeName().compareTo("resource") == 0) {
|
| 443 | return resource(node);
|
| 444 | } else { // look at the repository node (first in the file)
|
| 445 | if (node.getNodeName().compareTo("repository") == 0) {
|
| 446 | Date d = new Date();
|
| 447 | d.setTime(System.currentTimeMillis());
|
| 448 | NamedNodeMap nList = node.getAttributes();
|
| 449 | Node n = nList.getNamedItem("lastmodified");
|
| 450 | n.setNodeValue(m_format.format(d));
|
| 451 | }
|
| 452 | NodeList list = node.getChildNodes();
|
| 453 | if (list.getLength() > 0) {
|
| 454 | for (int i = 0; i < list.getLength(); i++) {
|
| 455 | if (walkOnTree(list.item(i))) { return true; }
|
| 456 | }
|
| 457 | }
|
| 458 | return false;
|
| 459 | }
|
| 460 |
|
| 461 | }
|
| 462 |
|
| 463 | /**
|
| 464 | * put the resource bundle in the tree.
|
| 465 | * @param node Node on the xml file
|
| 466 | * @param id id of the bundle ressource
|
| 467 | */
|
| 468 | private void searchRepository(Node node, int id) {
|
| 469 | if (node.getNodeName().compareTo("repository") == 0) {
|
| 470 | m_resourceBundle.setId(String.valueOf(id));
|
| 471 | node.appendChild(m_resourceBundle.getNode(m_repoDoc));
|
| 472 | return;
|
| 473 | } else {
|
| 474 | NodeList list = node.getChildNodes();
|
| 475 | if (list.getLength() > 0) {
|
| 476 | for (int i = 0; i < list.getLength(); i++) {
|
| 477 | searchRepository(list.item(i), id);
|
| 478 | }
|
| 479 | }
|
| 480 | }
|
| 481 |
|
| 482 | }
|
| 483 |
|
| 484 | /**
|
| 485 | * compare two node and update the array which compute the smallest free id.
|
| 486 | * @param node : node
|
| 487 | * @return true if the node is the same bundle than the ressourceBundle, else false.
|
| 488 | */
|
| 489 | private boolean resource(Node node) {
|
| 490 |
|
| 491 | // this part save all the id free if we need to add resource
|
| 492 | int id = Integer.parseInt(node.getAttributes().getNamedItem("id").getNodeValue());
|
| 493 | if (id >= m_idTab.length) {
|
| 494 | // resize tab
|
| 495 | boolean[] bt = new boolean[id + 1];
|
| 496 | for (int i = 0; i < id + 1; i++) {
|
| 497 | if (m_idTab.length > i && m_idTab[i]) {
|
| 498 | bt[i] = true;
|
| 499 | } else {
|
| 500 | bt[i] = false;
|
| 501 | }
|
| 502 | }
|
| 503 |
|
| 504 | m_idTab = bt;
|
| 505 | }
|
| 506 | m_idTab[id] = true;
|
| 507 |
|
| 508 | NamedNodeMap map = node.getAttributes();
|
| 509 |
|
| 510 | if (m_resourceBundle.isSameBundleResource(map.getNamedItem("symbolicname").getNodeValue(), map.getNamedItem("presentationname").getNodeValue(), map.getNamedItem("version").getNodeValue())) {
|
| 511 | m_resourceBundle.setId(String.valueOf(id));
|
| 512 | node.getParentNode().replaceChild(m_resourceBundle.getNode(m_repoDoc), node);
|
| 513 | return true;
|
| 514 | }
|
| 515 | return false;
|
| 516 | }
|
| 517 | }
|