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