    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.net.URI;
    24  import java.net.URL;
    25  import java.util.Arrays;
    26  import java.util.Iterator;
    27  import java.util.List;
    28  import java.util.regex.Matcher;
    29  import java.util.regex.Pattern;
    30
    31  import org.apache.maven.artifact.Artifact;
    32  import org.apache.maven.artifact.manager.WagonManager;
    33  import org.apache.maven.artifact.repository.ArtifactRepository;
    34  import org.apache.maven.plugin.AbstractMojo;
    35  import org.apache.maven.plugin.MojoExecutionException;
    36  import org.apache.maven.plugin.logging.Log;
    37  import org.apache.maven.project.MavenProject;
    38  import org.apache.maven.settings.Settings;
    39
    40
    41  /**
    42   * Deploys bundle details to a remote OBR repository (life-cycle goal)
    43   *
    44   * @goal deploy
    45   * @phase deploy
    46   * @threadSafe
    47   *
    48   * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
    49   */
    50  public final class ObrDeploy extends AbstractMojo
    51  {
    52      /**
    53       * When true, ignore remote locking.
    54       *
    55       * @parameter expression="${ignoreLock}"
    56       */
    57      private boolean ignoreLock;
    58
    59      /**
    60       * Optional public URL prefix for the remote repository.
    61       *
    62       * @parameter expression="${prefixUrl}"
    63       */
    64      private String prefixUrl;
    65
    66      /**
    67       * Optional public URL where the bundle has been deployed.
    68       *
    69       * @parameter expression="${bundleUrl}"
    70       */
    71      private String bundleUrl;
    72
    73      /**
    74       * Remote OBR Repository.
    75       *
    76       * @parameter expression="${remoteOBR}" default-value="NONE"
    77       */
    78      private String remoteOBR;
    79
    80      /**
    81       * Local OBR Repository.
    82       *
    83       * @parameter expression="${obrRepository}"
    84       */
    85      private String obrRepository;
    86
    87      /**
    88       * Project types which this plugin supports.
    89       *
    90       * @parameter
    91       */
    92      private List supportedProjectTypes = Arrays.asList( new String[]
    93          { "jar", "bundle" } );
    94
    95      /**
    96       * @parameter expression="${project.distributionManagementArtifactRepository}"
    97       * @readonly
    98       */
    99      private ArtifactRepository deploymentRepository;
    100
    101     /**
    102      * Alternative deployment repository. Format: id::layout::url
    103      *
    104      * @parameter expression="${altDeploymentRepository}"
    105      */
    106     private String altDeploymentRepository;
    107
    108     /**
    109      * OBR specific deployment repository. Format: id::layout::url
    110      *
    111      * @parameter expression="${obrDeploymentRepository}"
    112      */
    113     private String obrDeploymentRepository;
    114
    115     /**
    116      * Local Repository.
    117      *
    118      * @parameter expression="${localRepository}"
    119      * @required
    120      * @readonly
    121      */
    122     private ArtifactRepository localRepository;
    123
    124     /**
    125      * The Maven project.
    126      *
    127      * @parameter expression="${project}"
    128      * @required
    129      * @readonly
    130      */
    131     private MavenProject project;
    132
    133     /**
    134      * @parameter expression="${project.attachedArtifacts}
    135      * @required
    136      * @readonly
    137      */
    138     private List attachedArtifacts;
    139
    140     /**
    141      * Local Maven settings.
    142      *
    143      * @parameter expression="${settings}"
    144      * @required
    145      * @readonly
    146      */
    147     private Settings settings;
    148
    149     /**
    150      * The Wagon manager.
    151      *
    152      * @component
    153      */
    154     private WagonManager m_wagonManager;
    155
    156     /**
    157      * Attached source artifact
    158      */
    159     private Artifact m_sourceArtifact;
    160
    161     /**
    162      * Attached doc artifact
    163      */
    164     private Artifact m_docArtifact;
    165
    166
    167     public void execute() throws MojoExecutionException
    168     {
    169         String projectType = project.getPackaging();
    170
    171         // ignore unsupported project types, useful when bundleplugin is configured in parent pom
    172         if ( !supportedProjectTypes.contains( projectType ) )
    173         {
    174             getLog().warn(
    175                 "Ignoring project type " + projectType + " - supportedProjectTypes = " + supportedProjectTypes );
    176             return;
    177         }
    178         else if ( "NONE".equalsIgnoreCase( remoteOBR ) || "false".equalsIgnoreCase( remoteOBR ) )
    179         {
    180             getLog().info( "Remote OBR update disabled (enable with -DremoteOBR)" );
    181             return;
    182         }
    183
    184         // check for any attached sources or docs
    185         for ( Iterator i = attachedArtifacts.iterator(); i.hasNext(); )
    186         {
    187             Artifact artifact = ( Artifact ) i.next();
    188             if ( "sources".equals( artifact.getClassifier() ) )
    189             {
    190                 m_sourceArtifact = artifact;
    191             }
    192             else if ( "javadoc".equals( artifact.getClassifier() ) )
    193             {
    194                 m_docArtifact = artifact;
    195             }
    196         }
    197
    198         // if the user doesn't supply an explicit name for the remote OBR file, use the local name instead
    199         if ( null == remoteOBR || remoteOBR.trim().length() == 0 || "true".equalsIgnoreCase( remoteOBR ) )
    200         {
    201             remoteOBR = obrRepository;
    202         }
    203
    204         URI tempURI = ObrUtils.findRepositoryXml( "", remoteOBR );
    205         String repositoryName = new File( tempURI.getSchemeSpecificPart() ).getName();
    206
    207         Log log = getLog();
    208         ObrUpdate update;
    209
    210         RemoteFileManager remoteFile = new RemoteFileManager( m_wagonManager, settings, log );
    211         openRepositoryConnection( remoteFile );
    212
    213         // ======== LOCK REMOTE OBR ========
    214         log.info( "LOCK " + remoteFile + '/' + repositoryName );
    215         remoteFile.lockFile( repositoryName, ignoreLock );
    216         File downloadedRepositoryXml = null;
    217
    218         try
    219         {
    220             // ======== DOWNLOAD REMOTE OBR ========
    221             log.info( "Downloading " + repositoryName );
    222             downloadedRepositoryXml = remoteFile.get( repositoryName, ".xml" );
    223
    224             String mavenRepository = localRepository.getBasedir();
    225
    226             URI repositoryXml = downloadedRepositoryXml.toURI();
    227             URI obrXmlFile = ObrUtils.findObrXml( project );
    228
    229             Config userConfig = new Config();
    230             userConfig.setRemoteFile( true );
    231
    232             if ( bundleUrl != null )
    233             {
    234                 // public URL differs from the bundle file location
    235                 URI uri = URI.create( bundleUrl );
    236                 log.info( "Computed bundle uri: " + uri );
    237                 userConfig.setRemoteBundle( uri );
    238             }
    239             else if ( prefixUrl != null )
    240             {
    241                 // support absolute bundle URLs based on given prefix
    242                 URI bundleJar = ObrUtils.getArtifactURI( localRepository, project.getArtifact() );
    243                 String relative = ObrUtils.getRelativeURI( ObrUtils.toFileURI( mavenRepository ), bundleJar )
    244                     .toASCIIString();
    245                 URL resourceURL = new URL( new URL( prefixUrl + '/' ), relative );
    246                 URI uri = URI.create( resourceURL.toString() );
    247                 log.info( "Computed bundle uri: " + uri );
    248                 userConfig.setRemoteBundle( uri );
    249             }
    250
    251             update = new ObrUpdate( repositoryXml, obrXmlFile, project, mavenRepository, userConfig, log );
    252             update.parseRepositoryXml();
    253
    254             updateRemoteBundleMetadata( project.getArtifact(), update );
    255             for ( Iterator i = attachedArtifacts.iterator(); i.hasNext(); )
    256             {
    257                 updateRemoteBundleMetadata( ( Artifact ) i.next(), update );
    258             }
    259
    260             update.writeRepositoryXml();
    261
    262             if ( downloadedRepositoryXml.exists() )
    263             {
    264                 // ======== UPLOAD MODIFIED OBR ========
    265                 log.info( "Uploading " + repositoryName );
    266                 remoteFile.put( downloadedRepositoryXml, repositoryName );
    267             }
    268         }
    269         catch ( Exception e )
    270         {
    271             log.warn( "Exception while updating remote OBR: " + e.getLocalizedMessage(), e );
    272         }
    273         finally
    274         {
    275             // ======== UNLOCK REMOTE OBR ========
    276             log.info( "UNLOCK " + remoteFile + '/' + repositoryName );
    277             remoteFile.unlockFile( repositoryName );
    278             remoteFile.disconnect();
    279
    280             if ( null != downloadedRepositoryXml )
    281             {
    282                 downloadedRepositoryXml.delete();
    283             }
    284         }
    285     }
    286
    287     private static final Pattern ALT_REPO_SYNTAX_PATTERN = Pattern.compile( "(.+)::(.+)::(.+)" );
    288
    289
    290     private void openRepositoryConnection( RemoteFileManager remoteFile ) throws MojoExecutionException
    291     {
    292         // use OBR specific deployment location?
    293         if ( obrDeploymentRepository != null )
    294         {
    295             altDeploymentRepository = obrDeploymentRepository;
    296         }
    297
    298         if ( deploymentRepository == null && altDeploymentRepository == null )
    299         {
    300             String msg = "Deployment failed: repository element was not specified in the pom inside"
    301                 + " distributionManagement element or in -DaltDeploymentRepository=id::layout::url parameter";
    302
    303             throw new MojoExecutionException( msg );
    304         }
    305
    306         if ( altDeploymentRepository != null )
    307         {
    308             getLog().info( "Using alternate deployment repository " + altDeploymentRepository );
    309
    310             Matcher matcher = ALT_REPO_SYNTAX_PATTERN.matcher( altDeploymentRepository );
    311             if ( !matcher.matches() )
    312             {
    313                 throw new MojoExecutionException( "Invalid syntax for alternative repository \""
    314                     + altDeploymentRepository + "\". Use \"id::layout::url\"." );
    315             }
    316
    317             remoteFile.connect( matcher.group( 1 ).trim(), matcher.group( 3 ).trim() );
    318         }
    319         else
    320         {
    321             remoteFile.connect( deploymentRepository.getId(), deploymentRepository.getUrl() );
    322         }
    323     }
    324
    325
    326     private void updateRemoteBundleMetadata( Artifact artifact, ObrUpdate update ) throws MojoExecutionException
    327     {
    328         if ( !supportedProjectTypes.contains( artifact.getType() ) )
    329         {
    330             return;
    331         }
    332         else if ( null == artifact.getFile() || artifact.getFile().isDirectory() )
    333         {
    334             getLog().error( "No artifact found, try \"mvn install bundle:deploy\"" );
    335             return;
    336         }
    337
    338         URI bundleJar = ObrUtils.getArtifactURI( localRepository, artifact );
    339
    340         URI sourceJar = null;
    341         if ( null != m_sourceArtifact )
    342         {
    343             sourceJar = ObrUtils.getArtifactURI( localRepository, m_sourceArtifact );
    344         }
    345
    346         URI docJar = null;
    347         if ( null != m_docArtifact )
    348         {
    349             docJar = ObrUtils.getArtifactURI( localRepository, m_docArtifact );
    350         }
    351
    352         update.updateRepository( bundleJar, sourceJar, docJar );
    353     }
    354 }