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.obrplugin;
20  
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.OutputStreamWriter;
28  import java.io.Writer;
29  import java.net.URI;
30  
31  import org.apache.felix.bundlerepository.Resource;
32  import org.apache.felix.bundlerepository.impl.DataModelHelperImpl;
33  import org.apache.felix.bundlerepository.impl.PullParser;
34  import org.apache.felix.bundlerepository.impl.RepositoryImpl;
35  import org.apache.felix.bundlerepository.impl.RepositoryParser;
36  import org.apache.felix.bundlerepository.impl.ResourceImpl;
37  import org.apache.maven.plugin.MojoExecutionException;
38  import org.apache.maven.plugin.logging.Log;
39  import org.apache.maven.project.MavenProject;
40  import org.codehaus.plexus.util.FileUtils;
41  import org.kxml2.io.KXmlParser;
42  import org.xmlpull.v1.XmlPullParser;
43  
44  
45  /**
46   * this class parse the old repository.xml file build the bundle resource description and update the repository.
47   * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
48   */
49  public class ObrUpdate
50  {
51      /**
52       * logger for this plugin.
53       */
54      private Log m_logger;
55  
56      /**
57       * name and path to the repository descriptor file.
58       */
59      private URI m_repositoryXml;
60  
61      /**
62       * name and path to the obr.xml file.
63       */
64      private URI m_obrXml;
65  
66      /**
67       * maven project description.
68       */
69      private MavenProject m_project;
70  
71      /**
72       * user configuration information.
73       */
74      private Config m_userConfig;
75  
76      /**
77       * root on parent document.
78       */
79      private RepositoryImpl m_repository;
80  
81      /**
82       * used to store bundle information.
83       */
84      private ResourceImpl m_resourceBundle;
85  
86      /**
87       * base URI used to relativize bundle URIs.
88       */
89      private URI m_baseURI;
90  
91  
92      /**
93       * initialize information.
94       * @param repositoryXml path to the repository descriptor file
95       * @param obrXml path and filename to the obr.xml file
96       * @param project maven project description
97       * @param mavenRepositoryPath path to the local maven repository
98       * @param userConfig user information
99       * @param logger plugin logger
100      */
101     public ObrUpdate( URI repositoryXml, URI obrXml, MavenProject project, String mavenRepositoryPath,
102         Config userConfig, Log logger )
103     {
104         m_repositoryXml = repositoryXml;
105         m_obrXml = obrXml;
106         m_project = project;
107         m_logger = logger;
108 
109         m_userConfig = userConfig;
110 
111         if ( userConfig.isRemoteFile() )
112         {
113             m_baseURI = ObrUtils.toFileURI( mavenRepositoryPath );
114         }
115         else
116         {
117             m_baseURI = m_repositoryXml;
118         }
119     }
120 
121 
122     /**
123      * update the repository descriptor file. parse the old repository descriptor file,
124      * get the old reference of the bundle or determine the id for a new bundle, extract
125      * information from bindex set the new information in descriptor file and save it.
126      * 
127      * @param bundleJar path to the bundle jar file
128      * @param sourceJar path to the source jar file
129      * @param docJar path to the docs jar file
130      * 
131      * @throws MojoExecutionException if the plugin failed
132      */
133     public void updateRepository( URI bundleJar, URI sourceJar, URI docJar ) throws MojoExecutionException
134     {
135         m_logger.debug( " (f) repositoryXml = " + m_repositoryXml );
136         m_logger.debug( " (f) bundleJar = " + bundleJar );
137         m_logger.debug( " (f) sourceJar = " + sourceJar );
138         m_logger.debug( " (f) obrXml = " + m_obrXml );
139 
140         if ( m_repository == null )
141         {
142             return;
143         }
144 
145         // get the file size
146         File bundleFile = new File( bundleJar );
147         if ( bundleFile.exists() )
148         {
149             URI resourceURI = m_userConfig.getRemoteBundle();
150             if ( null == resourceURI )
151             {
152                 resourceURI = bundleJar;
153                 if ( m_userConfig.isPathRelative() )
154                 {
155                     resourceURI = ObrUtils.getRelativeURI( m_baseURI, resourceURI );
156                 }
157             }
158 
159             if ( m_userConfig.isRemoteFile() )
160             {
161                 m_logger.info( "Deploying " + resourceURI );
162             }
163             else
164             {
165                 m_logger.info( "Installing " + resourceURI );
166             }
167 
168             try
169             {
170                 m_resourceBundle = ( ResourceImpl ) new DataModelHelperImpl().createResource( bundleJar.toURL() );
171                 if ( m_resourceBundle == null )
172                 {
173                     return;
174                 }
175             }
176             catch ( IOException e )
177             {
178                 throw new MojoExecutionException( "Unable to load resource information", e );
179             }
180 
181             m_resourceBundle.put( Resource.SIZE, String.valueOf( bundleFile.length() ) );
182             m_resourceBundle.put( Resource.URI, resourceURI.toASCIIString() );
183         }
184         else
185         {
186             m_logger.error( "file doesn't exist: " + bundleJar );
187             return;
188         }
189 
190         // parse the obr.xml file
191         if ( m_obrXml != null )
192         {
193             m_logger.info( "Adding " + m_obrXml );
194 
195             // URL url = getClass().getResource("/SchemaObr.xsd");
196             // TODO validate obr.xml file
197 
198             // add contents to resource bundle
199             parseObrXml();
200         }
201 
202         String sourcePath = relativisePath( sourceJar );
203         String docPath = relativisePath( docJar );
204 
205         //        m_resourceBundle.construct( m_project, bindexExtractor, sourcePath, docPath );
206         //         TODO: rebuild wrt m_project
207 
208         m_repository.addResource( m_resourceBundle );
209         m_repository.setLastModified( System.currentTimeMillis() );
210     }
211 
212 
213     private String relativisePath( URI uri )
214     {
215         if ( null != uri )
216         {
217             if ( m_userConfig.isPathRelative() )
218             {
219                 return ObrUtils.getRelativeURI( m_baseURI, uri ).toASCIIString();
220             }
221 
222             return uri.toASCIIString();
223         }
224 
225         return null;
226     }
227 
228 
229     public void writeRepositoryXml() throws MojoExecutionException
230     {
231         m_logger.info( "Writing OBR metadata" );
232 
233         File file = null;
234         Writer writer;
235         try
236         {
237             file = File.createTempFile( "repository", ".xml" );
238             writer = new OutputStreamWriter( new FileOutputStream( file ) );
239         }
240         catch ( IOException e )
241         {
242             m_logger.error( "Unable to write to file: " + file.getName() );
243             e.printStackTrace();
244             throw new MojoExecutionException( "Unable to write to file: " + file.getName() + " : " + e.getMessage() );
245         }
246 
247         try
248         {
249             new DataModelHelperImpl().writeRepository( m_repository, writer );
250         }
251         catch ( IOException e )
252         {
253             throw new MojoExecutionException( "Unable to write repository xml", e );
254         }
255 
256         try
257         {
258             writer.flush();
259             writer.close();
260 
261             File outputFile = new File( m_repositoryXml );
262             outputFile.getParentFile().mkdirs();
263             FileUtils.rename( file, outputFile );
264         }
265         catch ( IOException e )
266         {
267             e.printStackTrace();
268             throw new MojoExecutionException( "IOException" );
269         }
270 
271     }
272 
273 
274     /**
275       * Parse the repository descriptor file.
276       *
277       * @throws MojoExecutionException if the plugin failed
278       */
279     public void parseRepositoryXml() throws MojoExecutionException
280     {
281         File fout = new File( m_repositoryXml );
282         if ( !fout.exists() )
283         {
284             m_repository = new RepositoryImpl();
285             writeRepositoryXml();
286         }
287         else
288         {
289             try
290             {
291                 m_repository = ( RepositoryImpl ) new DataModelHelperImpl().repository( m_repositoryXml.toURL() );
292             }
293             catch ( Exception e )
294             {
295                 throw new MojoExecutionException( "Unable to read repository xml: " + m_repositoryXml, e );
296             }
297         }
298     }
299 
300 
301     /**
302      * put the information from obr.xml into ressourceBundle object.
303      */
304     private void parseObrXml() throws MojoExecutionException
305     {
306         try
307         {
308             InputStream is = new FileInputStream( new File( m_obrXml ) );
309             try
310             {
311                 KXmlParser kxp = new KXmlParser();
312                 kxp.setInput( is, null );
313                 kxp.nextTag(); // skip top level element
314                 kxp.nextTag(); // go to first child element
315                 parseObrXml( kxp );
316             }
317             finally
318             {
319                 is.close();
320             }
321         }
322         catch ( Exception e )
323         {
324             throw new MojoExecutionException( "Unable to parse obr xml: " + m_obrXml, e );
325         }
326     }
327 
328 
329     private void parseObrXml( KXmlParser kxp ) throws Exception
330     {
331         PullParser parser = new PullParser();
332         while ( kxp.getEventType() == XmlPullParser.START_TAG )
333         {
334             if ( RepositoryParser.CATEGORY.equals( kxp.getName() ) )
335             {
336                 m_resourceBundle.addCategory( parser.parseCategory( kxp ) );
337             }
338             else if ( RepositoryParser.REQUIRE.equals( kxp.getName() ) )
339             {
340                 m_resourceBundle.addRequire( parser.parseRequire( kxp ) );
341             }
342             else if ( RepositoryParser.CAPABILITY.equals( kxp.getName() ) )
343             {
344                 m_resourceBundle.addCapability( parser.parseCapability( kxp ) );
345             }
346             else
347             {
348                 kxp.nextTag();
349                 parseObrXml( kxp );
350             }
351             kxp.nextTag();
352         }
353     }
354 
355 }