Add .tox/ to .gitignore
[odlparent.git] / karaf / karaf-maven-plugin / src / main / java / org / apache / karaf / tooling / features / AbstractFeatureMojo.java
1 /**
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one or more
4  * contributor license agreements.  See the NOTICE file distributed with
5  * this work for additional information regarding copyright ownership.
6  * The ASF licenses this file to You under the Apache License, Version 2.0
7  * (the "License"); you may not use this file except in compliance with
8  * 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, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.karaf.tooling.features;
19
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.apache.felix.utils.version.VersionRange;
29 import org.apache.felix.utils.version.VersionTable;
30 import org.apache.karaf.features.BundleInfo;
31 import org.apache.karaf.features.Conditional;
32 import org.apache.karaf.features.internal.model.Bundle;
33 import org.apache.karaf.features.internal.model.ConfigFile;
34 import org.apache.karaf.features.internal.model.Dependency;
35 import org.apache.karaf.features.internal.model.Feature;
36 import org.apache.karaf.features.internal.model.Features;
37 import org.apache.karaf.features.internal.model.JaxbUtil;
38 import org.apache.karaf.tooling.utils.MojoSupport;
39 import org.apache.maven.artifact.Artifact;
40 import org.apache.maven.artifact.repository.ArtifactRepository;
41 import org.apache.maven.plugin.MojoExecutionException;
42
43 import org.apache.maven.plugins.annotations.Parameter;
44 import org.osgi.framework.Version;
45
46 /**
47  * Common functionality for mojos that need to resolve features
48  */
49 public abstract class AbstractFeatureMojo extends MojoSupport {
50     
51     @Parameter
52     protected List<String> descriptors;
53     
54     protected Set<Artifact> descriptorArtifacts;
55
56     @Parameter
57     protected List<String> features;
58
59     @Parameter
60     protected boolean addTransitiveFeatures = true;
61
62     @Parameter
63     private boolean includeMvnBasedDescriptors = false;
64
65     @Parameter
66     private boolean failOnArtifactResolutionError = true;
67
68     @Parameter
69     private boolean resolveDefinedRepositoriesRecursively = true;
70
71     @Parameter
72     protected boolean skipNonMavenProtocols = true;
73
74     /**
75      * Ignore the dependency flag on the bundles in the features XML
76      */
77     @Parameter(defaultValue = "false")
78     protected boolean ignoreDependencyFlag;
79     
80     /**
81      * The start level exported when no explicit start level is set for a bundle
82      */
83     @Parameter
84     private int defaultStartLevel = 80;
85
86     /**
87      * Internal counter for garbage collection
88      */
89     private int resolveCount = 0;
90
91     public AbstractFeatureMojo() {
92         super();
93         descriptorArtifacts = new HashSet<Artifact>();
94     }
95
96     protected void addFeatureRepo(String featureUrl) throws MojoExecutionException {
97         Artifact featureDescArtifact = resourceToArtifact(featureUrl, true);
98         if (featureDescArtifact == null) {
99             return;
100         }
101         try {
102             resolveArtifact(featureDescArtifact, remoteRepos);
103             descriptors.add(0, featureUrl);
104         } catch (Exception e) {
105             getLog().warn("Can't add " + featureUrl + " in the descriptors set");
106             getLog().debug(e);
107         }
108     }
109
110     protected void retrieveDescriptorsRecursively(String uri, Set<String> bundles, Map<String, Feature> featuresMap) {
111         // let's ensure a mvn: based url is sitting in the local repo before we try reading it
112         Artifact descriptor;
113         try {
114             descriptor = resourceToArtifact(uri, true);
115         } catch (MojoExecutionException e) {
116             throw new RuntimeException(e.getMessage(), e);
117         }
118         if (descriptor != null) {
119             resolveArtifact(descriptor, remoteRepos);
120             descriptorArtifacts.add(descriptor);
121         }
122         if (includeMvnBasedDescriptors) {
123             bundles.add(uri);
124         }
125         Features repo = JaxbUtil.unmarshal(translateFromMaven(uri.replaceAll(" ", "%20")), true);
126         for (Feature f : repo.getFeature()) {
127             featuresMap.put(f.getId(), f);
128         }
129         if (resolveDefinedRepositoriesRecursively) {
130             for (String r : repo.getRepository()) {
131                 retrieveDescriptorsRecursively(r, bundles, featuresMap);
132             }
133         }
134     }
135
136     /**
137      * Resolves and copies the given artifact to the repository path.
138      * Prefers to resolve using the repository of the artifact if present.
139      * 
140      * @param artifact
141      * @param remoteRepos
142      */
143     @SuppressWarnings("deprecation")
144     protected void resolveArtifact(Artifact artifact, List<ArtifactRepository> remoteRepos) {
145         try {
146             if (artifact == null) {
147                 return;
148             }
149             List<ArtifactRepository> usedRemoteRepos = artifact.getRepository() != null ? 
150                     Collections.singletonList(artifact.getRepository())
151                     : remoteRepos;
152             artifactResolver.resolve(artifact, usedRemoteRepos, localRepo);
153         } catch (Exception e) {
154             if (failOnArtifactResolutionError) {
155                 throw new RuntimeException("Can't resolve artifact " + artifact, e);
156             }
157             getLog().warn("Can't resolve artifact " + artifact);
158             getLog().debug(e);
159         }
160     }
161
162
163     /**
164      * Populate the features by traversing the listed features and their
165      * dependencies if transitive is true
166      *  
167      * @param featureNames
168      * @param features
169      * @param featuresMap
170      * @param transitive
171      */
172     protected void addFeatures(List<String> featureNames, Set<Feature> features, Map<String, Feature> featuresMap, boolean transitive) {
173         for (String feature : featureNames) {
174             String[] split = feature.split("/");
175             Feature f = getMatchingFeature(featuresMap, split[0], split.length > 1 ? split[1] : null);
176             features.add(f);
177             if (transitive) {
178                 addFeaturesDependencies(f.getFeature(), features, featuresMap, true);
179             }
180         }
181     }
182
183     protected void addFeaturesDependencies(List<Dependency> featureNames, Set<Feature> features, Map<String, Feature> featuresMap, boolean transitive) {
184         for (Dependency dependency : featureNames) {
185             Feature f = getMatchingFeature(featuresMap, dependency.getName(), dependency.getVersion());
186             features.add(f);
187             if (transitive) {
188                 addFeaturesDependencies(f.getFeature(), features, featuresMap, true);
189             }
190         }
191     }
192
193     private Feature getMatchingFeature(Map<String, Feature> featuresMap, String feature, String version) {
194         Feature f = null;
195         if (version != null && !version.equals(Feature.DEFAULT_VERSION)) {
196             // looking for a specific feature with name and version
197             f = featuresMap.get(feature + "/" + version);
198             if (f == null) {
199                 //it's probably is a version range so try to use VersionRange Utils
200                 VersionRange versionRange = new VersionRange(version);
201                 for (String key : featuresMap.keySet()) {
202                     String[] nameVersion = key.split("/");
203                     if (feature.equals(nameVersion[0])) {
204                         String verStr = featuresMap.get(key).getVersion();
205                         Version ver = VersionTable.getVersion(verStr);
206                         if (versionRange.contains(ver)) {
207                             if (f == null || VersionTable.getVersion(f.getVersion()).compareTo(VersionTable.getVersion(featuresMap.get(key).getVersion())) < 0) {    
208                                 f = featuresMap.get(key);
209                             }
210                         }
211                     }
212                 }
213             }
214         } else {
215             // looking for the first feature name (whatever the version is)
216             for (String key : featuresMap.keySet()) {
217                 String[] nameVersion = key.split("/");
218                 if (feature.equals(nameVersion[0])) {
219                     f = featuresMap.get(key);
220                     break;
221                 }
222             }
223         }
224         if (f == null) {
225             throw new IllegalArgumentException("Unable to find the feature '" + feature + "'");
226         }
227         return f;
228     }
229
230     protected Set<Feature> resolveFeatures() throws MojoExecutionException {
231         Set<Feature> featuresSet = new HashSet<Feature>();
232         try {
233             Set<String> artifactsToCopy = new HashSet<String>();
234             Map<String, Feature> featuresMap = new HashMap<String, Feature>();
235             for (String uri : descriptors) {
236                 retrieveDescriptorsRecursively(uri, artifactsToCopy, featuresMap);
237             }
238     
239             // no features specified, handle all of them
240             if (features == null) {
241                 features = new ArrayList<String>(featuresMap.keySet());
242             }
243             
244             addFeatures(features, featuresSet, featuresMap, addTransitiveFeatures);
245     
246             getLog().info("Using local repository at: " + localRepo.getUrl());
247             for (Feature feature : featuresSet) {
248                 try {
249                     for (Bundle bundle : feature.getBundle()) {
250                         resolveArtifact(bundle.getLocation());
251                     }
252                     for (Conditional conditional : feature.getConditional()) {
253                         for (BundleInfo bundle : conditional.getBundles()) {
254                             if (ignoreDependencyFlag || (!ignoreDependencyFlag && !bundle.isDependency())) {
255                                 resolveArtifact(bundle.getLocation());
256                             }
257                         }
258                     }
259                     for (ConfigFile configfile : feature.getConfigfile()) {
260                         resolveArtifact(configfile.getLocation());
261                     }
262                 } catch (RuntimeException e) {
263                     throw new RuntimeException("Error resolving feature " + feature.getName() + "/" + feature.getVersion(), e);
264                 }
265             }            
266         } catch (Exception e) {
267             throw new MojoExecutionException("Error populating repository", e);
268         }
269         return featuresSet;
270     }
271
272     private Artifact resolveArtifact(String location) throws MojoExecutionException {
273         Artifact artifact = resourceToArtifact(location, skipNonMavenProtocols);
274         if (artifact != null) {
275             try {
276                 resolveArtifact(artifact, remoteRepos);
277             } catch (RuntimeException e) {
278                 throw new RuntimeException("Error resolving artifact " + location, e);
279             }
280         }
281         checkDoGarbageCollect();
282         return artifact;
283     }
284
285     /**
286      * Maven ArtifactResolver leaves file handles around so need to clean up
287      * or we will run out of file descriptors
288      */
289     protected void checkDoGarbageCollect() {
290         if (this.resolveCount++ % 100 == 0) {
291             System.gc();
292             System.runFinalization();
293         }
294     }
295
296
297 }