blob: 468b956482fa422841f41a0a575724f71fc77873 [file] [log] [blame]
Stuart McCullochb657c2f2008-01-25 08:10:25 +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.obr.plugin;
20
21
22import java.io.File;
23import java.io.FileNotFoundException;
24import java.io.FileOutputStream;
25import java.io.IOException;
26import java.net.URI;
27import java.text.SimpleDateFormat;
28import java.util.Date;
29import java.util.Properties;
30
31import javax.xml.parsers.DocumentBuilder;
32import javax.xml.parsers.DocumentBuilderFactory;
33import javax.xml.parsers.ParserConfigurationException;
34import javax.xml.transform.Result;
35import javax.xml.transform.Transformer;
36import javax.xml.transform.TransformerConfigurationException;
37import javax.xml.transform.TransformerException;
38import javax.xml.transform.TransformerFactory;
39import javax.xml.transform.dom.DOMSource;
40import javax.xml.transform.stream.StreamResult;
41
42import org.apache.maven.plugin.MojoExecutionException;
43import org.apache.maven.plugin.logging.Log;
44import org.apache.maven.project.MavenProject;
45import org.w3c.dom.Document;
46import org.w3c.dom.Element;
47import org.w3c.dom.NamedNodeMap;
48import org.w3c.dom.Node;
49import org.w3c.dom.NodeList;
50import org.xml.sax.SAXException;
51
52
53/**
54 * this class parse the old repository.xml file build the bundle resource description and update the repository.
55 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
56 */
Stuart McCullochb657c2f2008-01-25 08:10:25 +000057public class ObrUpdate
58{
59 /**
60 * generate the date format to insert it in repository descriptor file.
61 */
62 static SimpleDateFormat m_format = new SimpleDateFormat( "yyyyMMddHHmmss.SSS" );
63
64 /**
65 * logger for this plugin.
66 */
67 private Log m_logger;
68
69 /**
70 * name and path to the repository descriptor file.
71 */
Stuart McCullochb721aae2008-01-27 08:16:22 +000072 private URI m_repositoryXml;
Stuart McCullochb657c2f2008-01-25 08:10:25 +000073
74 /**
75 * name and path to the obr.xml file.
76 */
Stuart McCulloch80a87342008-01-27 11:04:46 +000077 private URI m_obrXml;
Stuart McCullochb657c2f2008-01-25 08:10:25 +000078
79 /**
80 * name and path to the bundle jar file.
81 */
Stuart McCulloch80a87342008-01-27 11:04:46 +000082 private URI m_bundlePath;
Stuart McCullochb657c2f2008-01-25 08:10:25 +000083
84 /**
85 * maven project description.
86 */
87 private MavenProject m_project;
88
89 /**
90 * user configuration information.
91 */
92 private Config m_userConfig;
93
94 /**
95 * used to build another xml document.
96 */
Stuart McCullochb721aae2008-01-27 08:16:22 +000097 private DocumentBuilder m_documentBuilder;
Stuart McCullochb657c2f2008-01-25 08:10:25 +000098
99 /**
100 * root on parent document.
101 */
Stuart McCullochb721aae2008-01-27 08:16:22 +0000102 private Document m_repositoryDoc;
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000103
104 /**
105 * used to store bundle information.
106 */
107 private ResourcesBundle m_resourceBundle;
108
109 /**
Stuart McCulloch80a87342008-01-27 11:04:46 +0000110 * base URI used to relativize bundle URIs.
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000111 */
Stuart McCulloch80a87342008-01-27 11:04:46 +0000112 private URI m_baseURI;
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000113
114
115 /**
116 * initialize information.
Stuart McCullochb721aae2008-01-27 08:16:22 +0000117 * @param repositoryXml path to the repository descriptor file
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000118 * @param obrXml path and filename to the obr.xml file
119 * @param project maven project description
Stuart McCullochb721aae2008-01-27 08:16:22 +0000120 * @param bundlePath path to the bundle jar file
121 * @param mavenRepositoryPath path to the local maven repository
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000122 * @param userConfig user information
Stuart McCullochb721aae2008-01-27 08:16:22 +0000123 * @param logger plugin logger
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000124 */
Stuart McCulloch473e9942008-01-28 05:23:50 +0000125 public ObrUpdate( PathFile repositoryXml, URI obrXml, MavenProject project, String bundlePath,
Stuart McCullochb721aae2008-01-27 08:16:22 +0000126 String mavenRepositoryPath, Config userConfig, Log logger )
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000127 {
Stuart McCullochbb9bd382008-01-25 08:14:10 +0000128 // m_localRepo = localRepo;
Stuart McCulloch80a87342008-01-27 11:04:46 +0000129 m_bundlePath = ObrUtils.toFileURI( bundlePath );
130 m_repositoryXml = repositoryXml.getFile().toURI(); // FIXME: remove when PathFile is gone
Stuart McCulloch473e9942008-01-28 05:23:50 +0000131 m_obrXml = obrXml;
Stuart McCullochbb9bd382008-01-25 08:14:10 +0000132 m_project = project;
Stuart McCullochb721aae2008-01-27 08:16:22 +0000133 m_logger = logger;
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000134
Stuart McCullochbb9bd382008-01-25 08:14:10 +0000135 m_userConfig = userConfig;
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000136
Stuart McCullochb721aae2008-01-27 08:16:22 +0000137 m_resourceBundle = new ResourcesBundle( logger );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000138
139 if ( userConfig.isRemotely() )
140 {
Stuart McCulloch80a87342008-01-27 11:04:46 +0000141 m_baseURI = ObrUtils.toFileURI( mavenRepositoryPath );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000142 }
143 else
144 {
Stuart McCulloch80a87342008-01-27 11:04:46 +0000145 m_baseURI = m_repositoryXml;
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000146 }
147 }
148
149
150 /**
151 * 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.
152 * @throws MojoExecutionException if the plugin failed
153 */
154 public void updateRepository() throws MojoExecutionException
155 {
156
157 m_logger.debug( " (f) m_obrXml = " + m_obrXml );
Stuart McCullochb721aae2008-01-27 08:16:22 +0000158 m_logger.debug( " (f) m_bundlePath = " + m_bundlePath );
159 m_logger.debug( " (f) m_repositoryXml = " + m_repositoryXml );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000160
Stuart McCullochb721aae2008-01-27 08:16:22 +0000161 m_documentBuilder = initDocumentBuilder();
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000162
Stuart McCullochb721aae2008-01-27 08:16:22 +0000163 if ( m_documentBuilder == null )
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000164 {
165 return;
166 }
167
168 // get the file size
Stuart McCulloch80a87342008-01-27 11:04:46 +0000169 File bundleFile = new File( m_bundlePath );
Stuart McCullochb721aae2008-01-27 08:16:22 +0000170 if ( bundleFile.exists() )
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000171 {
Stuart McCulloch80a87342008-01-27 11:04:46 +0000172 URI bundleURI = m_bundlePath;
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000173 if ( m_userConfig.isPathRelative() )
174 {
Stuart McCulloch80a87342008-01-27 11:04:46 +0000175 bundleURI = ObrUtils.getRelativeURI( m_baseURI, bundleURI );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000176 }
Stuart McCulloch80a87342008-01-27 11:04:46 +0000177
178 m_resourceBundle.setSize( String.valueOf( bundleFile.length() ) );
179 m_resourceBundle.setUri( bundleURI.toASCIIString() );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000180 }
181 else
182 {
Stuart McCullochb721aae2008-01-27 08:16:22 +0000183 m_logger.error( "file doesn't exist: " + m_bundlePath );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000184 return;
185 }
186
187 // parse repository
188 if ( parseRepositoryXml() == -1 )
189 {
190 return;
191 }
192
193 // parse the obr.xml file
194 if ( m_obrXml != null )
195 {
196 // URL url = getClass().getResource("/SchemaObr.xsd");
197 // TODO validate obr.xml file
198
Stuart McCullochb721aae2008-01-27 08:16:22 +0000199 Document obrXmlDoc = parseFile( m_obrXml, m_documentBuilder );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000200 if ( obrXmlDoc == null )
201 {
202 return;
203 }
204 Node obrXmlRoot = ( Element ) obrXmlDoc.getDocumentElement();
205 // sort the obr file
206 sortObrXml( obrXmlRoot );
207 }
208
Stuart McCullochb721aae2008-01-27 08:16:22 +0000209 ExtractBindexInfo bindexExtractor;
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000210 try
211 {
Stuart McCullochb721aae2008-01-27 08:16:22 +0000212 // use bindex to extract bundle information
Stuart McCulloch80a87342008-01-27 11:04:46 +0000213 bindexExtractor = new ExtractBindexInfo( m_repositoryXml, m_bundlePath.getPath() );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000214 }
215 catch ( MojoExecutionException e )
216 {
217 m_logger.error( "unable to build Bindex informations" );
218 e.printStackTrace();
219 throw new MojoExecutionException( "MojoFailureException" );
220 }
221
Stuart McCullochb721aae2008-01-27 08:16:22 +0000222 m_resourceBundle.construct( m_project, bindexExtractor );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000223
Stuart McCullochb721aae2008-01-27 08:16:22 +0000224 Element rootElement = m_repositoryDoc.getDocumentElement();
225 if ( !walkOnTree( rootElement ) )
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000226 {
227 // the correct resource node was not found, we must create it
228 // we compute the new id
229 String id = m_resourceBundle.getId();
Stuart McCullochb721aae2008-01-27 08:16:22 +0000230 searchRepository( rootElement, id );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000231 }
232
233 // the repository.xml file have been modified, so we save it
234 m_logger.info( "Writing OBR metadata" );
Stuart McCullochb721aae2008-01-27 08:16:22 +0000235 writeToFile( m_repositoryXml, rootElement );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000236 }
237
238
239 /**
240 * init the document builder from xerces.
241 * @return DocumentBuilder ready to create new document
242 */
Stuart McCullochb721aae2008-01-27 08:16:22 +0000243 private DocumentBuilder initDocumentBuilder()
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000244 {
Stuart McCullochb721aae2008-01-27 08:16:22 +0000245 DocumentBuilder documentBuilder = null;
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000246 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
247 try
248 {
Stuart McCullochb721aae2008-01-27 08:16:22 +0000249 documentBuilder = factory.newDocumentBuilder();
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000250 }
251 catch ( ParserConfigurationException e )
252 {
253 m_logger.error( "unable to create a new xml document" );
254 e.printStackTrace();
255 }
Stuart McCullochb721aae2008-01-27 08:16:22 +0000256 return documentBuilder;
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000257
258 }
259
260
261 /**
262 * Parse the repository descriptor file.
263 *
264 * @return 0 if the bundle is already in the descriptor, else -1
265 * @throws MojoExecutionException if the plugin failed
266 */
267 private int parseRepositoryXml() throws MojoExecutionException
268 {
269
Stuart McCullochb721aae2008-01-27 08:16:22 +0000270 File fout = new File( m_repositoryXml );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000271 if ( !fout.exists() )
272 {
Stuart McCullochb721aae2008-01-27 08:16:22 +0000273 Document doc = m_documentBuilder.newDocument();
Stuart McCulloch80a87342008-01-27 11:04:46 +0000274
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000275 // 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 {
Stuart McCullochb721aae2008-01-27 08:16:22 +0000283 writeToFile( m_repositoryXml, root );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000284 }
285 catch ( MojoExecutionException e )
286 {
287 e.printStackTrace();
288 throw new MojoExecutionException( "MojoExecutionException" );
289 }
290 }
291
292 // now we parse the repository.xml file
Stuart McCulloch80a87342008-01-27 11:04:46 +0000293 m_repositoryDoc = parseFile( m_repositoryXml, m_documentBuilder );
Stuart McCullochb721aae2008-01-27 08:16:22 +0000294 if ( m_repositoryDoc == null )
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000295 {
296 return -1;
297 }
298
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000299 return 0;
300 }
301
302
303 /**
304 * transform a xml file to a xerces Document.
305 * @param filename path to the xml file
Stuart McCullochb721aae2008-01-27 08:16:22 +0000306 * @param documentBuilder DocumentBuilder get from xerces
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000307 * @return Document which describe this file
308 */
Stuart McCulloch80a87342008-01-27 11:04:46 +0000309 private Document parseFile( URI filename, DocumentBuilder documentBuilder )
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000310 {
Stuart McCullochb721aae2008-01-27 08:16:22 +0000311 if ( documentBuilder == null )
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000312 {
313 return null;
314 }
315 // The document is the root of the DOM tree.
316 m_logger.info( "Parsing " + filename );
317 Document doc = null;
318 try
319 {
Stuart McCullochb721aae2008-01-27 08:16:22 +0000320 doc = documentBuilder.parse( new File( filename ) );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000321 }
322 catch ( SAXException e )
323 {
324 e.printStackTrace();
325 return null;
326 }
327 catch ( IOException e )
328 {
329 m_logger.error( "cannot open file: " + filename );
330 e.printStackTrace();
331 return null;
332 }
333 return doc;
334 }
335
336
337 /**
338 * put the information from obr.xml into ressourceBundle object.
339 * @param node Node to the OBR.xml file
340 */
341 private void sortObrXml( Node node )
342 {
343 if ( node.getNodeName().compareTo( "require" ) == 0 )
344 {
345 Require newRequireNode = new Require();
346 NamedNodeMap list = node.getAttributes();
347 try
348 {
349 newRequireNode.setExtend( list.getNamedItem( "extend" ).getNodeValue() );
350 newRequireNode.setMultiple( list.getNamedItem( "multiple" ).getNodeValue() );
351 newRequireNode.setOptional( list.getNamedItem( "optional" ).getNodeValue() );
352 newRequireNode.setFilter( list.getNamedItem( "filter" ).getNodeValue() );
353 newRequireNode.setName( list.getNamedItem( "name" ).getNodeValue() );
354 }
355 catch ( NullPointerException e )
356 {
357 m_logger
358 .error( "the obr.xml file seems to be invalid in a \"require\" tag (one or more attributes are missing)" );
359 // e.printStackTrace();
360 }
361 newRequireNode.setValue( XmlHelper.getTextContent( node ) );
362 m_resourceBundle.addRequire( newRequireNode );
363 }
364 else if ( node.getNodeName().compareTo( "capability" ) == 0 )
365 {
366 Capability newCapability = new Capability();
367 try
368 {
369 newCapability.setName( node.getAttributes().getNamedItem( "name" ).getNodeValue() );
370 }
371 catch ( NullPointerException e )
372 {
373 m_logger.error( "attribute \"name\" is missing in obr.xml in a \"capability\" tag" );
374 e.printStackTrace();
375 }
376 NodeList list = node.getChildNodes();
377 for ( int i = 0; i < list.getLength(); i++ )
378 {
379 PElement p = new PElement();
380 Node n = list.item( i );
381 Node item = null;
382 // System.err.println(n.getNodeName());
383 if ( n.getNodeName().compareTo( "p" ) == 0 )
384 {
385
386 p.setN( n.getAttributes().getNamedItem( "n" ).getNodeValue() );
387 item = n.getAttributes().getNamedItem( "t" );
388 if ( item != null )
389 {
390 p.setT( item.getNodeValue() );
391 }
392 item = n.getAttributes().getNamedItem( "v" );
393 if ( item != null )
394 {
395 p.setV( item.getNodeValue() );
396 }
397
398 newCapability.addP( p );
399 }
400 }
401 m_resourceBundle.addCapability( newCapability );
402 }
403 else if ( node.getNodeName().compareTo( "category" ) == 0 )
404 {
405 Category newCategory = new Category();
406 newCategory.setId( node.getAttributes().getNamedItem( "id" ).getNodeValue() );
407 m_resourceBundle.addCategory( newCategory );
408 }
409 else
410 {
411 NodeList list = node.getChildNodes();
412 for ( int i = 0; i < list.getLength(); i++ )
413 {
414 sortObrXml( list.item( i ) );
415 }
416 }
417 }
418
419
420 /**
421 * write a Node in a xml file.
422 * @param outputFilename URI to the output file
423 * @param treeToBeWrite Node root of the tree to be write in file
424 * @throws MojoExecutionException if the plugin failed
425 */
426 private void writeToFile( URI outputFilename, Node treeToBeWrite ) throws MojoExecutionException
427 {
428 // init the transformer
429 Transformer transformer = null;
430 TransformerFactory tfabrique = TransformerFactory.newInstance();
431 try
432 {
433 transformer = tfabrique.newTransformer();
434 }
435 catch ( TransformerConfigurationException e )
436 {
437 m_logger.error( "Unable to write to file: " + outputFilename.toString() );
438 e.printStackTrace();
439 throw new MojoExecutionException( "TransformerConfigurationException" );
440 }
441 Properties proprietes = new Properties();
442 proprietes.put( "method", "xml" );
443 proprietes.put( "version", "1.0" );
444 proprietes.put( "encoding", "ISO-8859-1" );
445 proprietes.put( "standalone", "yes" );
446 proprietes.put( "indent", "yes" );
447 proprietes.put( "omit-xml-declaration", "no" );
448 transformer.setOutputProperties( proprietes );
449
450 DOMSource input = new DOMSource( treeToBeWrite );
451
452 File fichier = new File( outputFilename );
Stuart McCulloch80a87342008-01-27 11:04:46 +0000453 fichier.getParentFile().mkdirs();
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000454 FileOutputStream flux = null;
455 try
456 {
457 flux = new FileOutputStream( fichier );
458 }
459 catch ( FileNotFoundException e )
460 {
461 m_logger.error( "Unable to write to file: " + fichier.getName() );
462 e.printStackTrace();
463 throw new MojoExecutionException( "FileNotFoundException" );
464 }
465 Result output = new StreamResult( flux );
466 try
467 {
468 transformer.transform( input, output );
469 }
470 catch ( TransformerException e )
471 {
472 e.printStackTrace();
473 throw new MojoExecutionException( "TransformerException" );
474 }
475
476 try
477 {
478 flux.flush();
479 flux.close();
480 }
481 catch ( IOException e )
482 {
483 e.printStackTrace();
484 throw new MojoExecutionException( "IOException" );
485 }
486
487 }
488
489
490 /**
491 * walk on the tree until the targeted node was found.
492 * @param node targeted node
493 * @return true if the requiered node was found else false.
494 */
495 private boolean walkOnTree( Node node )
496 {
497
498 if ( node.getNodeName().compareTo( "resource" ) == 0 )
499 {
500 return resource( node );
501 }
502 else
503 { // look at the repository node (first in the file)
504 if ( node.getNodeName().compareTo( "repository" ) == 0 )
505 {
506 Date d = new Date();
507 d.setTime( System.currentTimeMillis() );
508 NamedNodeMap nList = node.getAttributes();
509 Node n = nList.getNamedItem( "lastmodified" );
510 n.setNodeValue( m_format.format( d ) );
511 }
512 NodeList list = node.getChildNodes();
513 if ( list.getLength() > 0 )
514 {
515 for ( int i = 0; i < list.getLength(); i++ )
516 {
517 if ( walkOnTree( list.item( i ) ) )
518 {
519 return true;
520 }
521 }
522 }
523 return false;
524 }
525
526 }
527
528
529 /**
530 * put the resource bundle in the tree.
531 * @param node Node on the xml file
532 * @param id id of the bundle ressource
533 */
534 private void searchRepository( Node node, String id )
535 {
536 if ( node.getNodeName().compareTo( "repository" ) == 0 )
537 {
Stuart McCullochb721aae2008-01-27 08:16:22 +0000538 node.appendChild( m_resourceBundle.getNode( m_repositoryDoc ) );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000539 return;
540 }
541 else
542 {
543 System.out.println( "Second branch..." );
544 NodeList list = node.getChildNodes();
545 if ( list.getLength() > 0 )
546 {
547 for ( int i = 0; i < list.getLength(); i++ )
548 {
549 searchRepository( list.item( i ), id );
550 }
551 }
552 }
553
554 }
555
556
557 /**
558 * compare two node and update the array which compute the smallest free id.
559 * @param node : node
560 * @return true if the node is the same bundle than the ressourceBundle, else false.
561 */
562 private boolean resource( Node node )
563 {
564
565 // this part save all the id free if we need to add resource
566 String id = node.getAttributes().getNamedItem( "id" ).getNodeValue();
567 NamedNodeMap map = node.getAttributes();
568
569 if ( m_resourceBundle.isSameBundleResource( map.getNamedItem( "symbolicname" ).getNodeValue(), map
570 .getNamedItem( "version" ).getNodeValue() ) )
571 {
572 m_resourceBundle.setId( String.valueOf( id ) );
Stuart McCullochb721aae2008-01-27 08:16:22 +0000573 node.getParentNode().replaceChild( m_resourceBundle.getNode( m_repositoryDoc ), node );
Stuart McCullochb657c2f2008-01-25 08:10:25 +0000574 return true;
575 }
576 return false;
577 }
578}