blob: 017fe083dff54ba9b2e9d079eb833e28274bf483 [file] [log] [blame]
Stefano Lenzi476013d2007-09-21 23:59:54 +00001/*
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 */
19package org.apache.felix.sandbox.obr.plugin;
20
21import java.io.File;
22import java.io.FileNotFoundException;
23import java.io.FileOutputStream;
24import java.io.IOException;
25import java.net.URI;
26import java.text.SimpleDateFormat;
27import java.util.Date;
28import java.util.Properties;
29
30import javax.xml.parsers.DocumentBuilder;
31import javax.xml.parsers.DocumentBuilderFactory;
32import javax.xml.parsers.ParserConfigurationException;
33import javax.xml.transform.Result;
34import javax.xml.transform.Transformer;
35import javax.xml.transform.TransformerConfigurationException;
36import javax.xml.transform.TransformerException;
37import javax.xml.transform.TransformerFactory;
38import javax.xml.transform.dom.DOMSource;
39import javax.xml.transform.stream.StreamResult;
40
41import org.apache.maven.plugin.MojoExecutionException;
42import org.apache.maven.plugin.logging.Log;
43import org.apache.maven.project.MavenProject;
44import org.w3c.dom.Document;
45import org.w3c.dom.Element;
46import org.w3c.dom.NamedNodeMap;
47import org.w3c.dom.Node;
48import org.w3c.dom.NodeList;
49import 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
56public 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 McCullochbfc05cf2007-09-26 15:34:00 +0000340 newRequireNode.setValue(XmlHelper.getTextContent(node));
Stefano Lenzi476013d2007-09-21 23:59:54 +0000341 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}