    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 }