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 }