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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org.apache.karaf.tooling.features;
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;
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;
43 import org.apache.maven.plugins.annotations.Parameter;
44 import org.osgi.framework.Version;
47 * Common functionality for mojos that need to resolve features
49 public abstract class AbstractFeatureMojo extends MojoSupport {
52 protected List<String> descriptors;
54 protected Set<Artifact> descriptorArtifacts;
57 protected List<String> features;
60 protected boolean addTransitiveFeatures = true;
63 private boolean includeMvnBasedDescriptors = false;
66 private boolean failOnArtifactResolutionError = true;
69 private boolean resolveDefinedRepositoriesRecursively = true;
72 protected boolean skipNonMavenProtocols = true;
75 * Ignore the dependency flag on the bundles in the features XML
77 @Parameter(defaultValue = "false")
78 protected boolean ignoreDependencyFlag;
81 * The start level exported when no explicit start level is set for a bundle
84 private int defaultStartLevel = 80;
87 * Internal counter for garbage collection
89 private int resolveCount = 0;
91 public AbstractFeatureMojo() {
93 descriptorArtifacts = new HashSet<Artifact>();
96 protected void addFeatureRepo(String featureUrl) throws MojoExecutionException {
97 Artifact featureDescArtifact = resourceToArtifact(featureUrl, true);
98 if (featureDescArtifact == null) {
102 resolveArtifact(featureDescArtifact, remoteRepos);
103 descriptors.add(0, featureUrl);
104 } catch (Exception e) {
105 getLog().warn("Can't add " + featureUrl + " in the descriptors set");
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
114 descriptor = resourceToArtifact(uri, true);
115 } catch (MojoExecutionException e) {
116 throw new RuntimeException(e.getMessage(), e);
118 if (descriptor != null) {
119 resolveArtifact(descriptor, remoteRepos);
120 descriptorArtifacts.add(descriptor);
122 if (includeMvnBasedDescriptors) {
125 Features repo = JaxbUtil.unmarshal(translateFromMaven(uri.replaceAll(" ", "%20")), true);
126 for (Feature f : repo.getFeature()) {
127 featuresMap.put(f.getId(), f);
129 if (resolveDefinedRepositoriesRecursively) {
130 for (String r : repo.getRepository()) {
131 retrieveDescriptorsRecursively(r, bundles, featuresMap);
137 * Resolves and copies the given artifact to the repository path.
138 * Prefers to resolve using the repository of the artifact if present.
143 @SuppressWarnings("deprecation")
144 protected void resolveArtifact(Artifact artifact, List<ArtifactRepository> remoteRepos) {
146 if (artifact == null) {
149 List<ArtifactRepository> usedRemoteRepos = artifact.getRepository() != null ?
150 Collections.singletonList(artifact.getRepository())
152 artifactResolver.resolve(artifact, usedRemoteRepos, localRepo);
153 } catch (Exception e) {
154 if (failOnArtifactResolutionError) {
155 throw new RuntimeException("Can't resolve artifact " + artifact, e);
157 getLog().warn("Can't resolve artifact " + artifact);
164 * Populate the features by traversing the listed features and their
165 * dependencies if transitive is true
167 * @param featureNames
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);
178 addFeaturesDependencies(f.getFeature(), features, featuresMap, true);
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());
188 addFeaturesDependencies(f.getFeature(), features, featuresMap, true);
193 private Feature getMatchingFeature(Map<String, Feature> featuresMap, String feature, String version) {
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);
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);
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);
225 throw new IllegalArgumentException("Unable to find the feature '" + feature + "'");
230 protected Set<Feature> resolveFeatures() throws MojoExecutionException {
231 Set<Feature> featuresSet = new HashSet<Feature>();
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);
239 // no features specified, handle all of them
240 if (features == null) {
241 features = new ArrayList<String>(featuresMap.keySet());
244 addFeatures(features, featuresSet, featuresMap, addTransitiveFeatures);
246 getLog().info("Using local repository at: " + localRepo.getUrl());
247 for (Feature feature : featuresSet) {
249 for (Bundle bundle : feature.getBundle()) {
250 resolveArtifact(bundle.getLocation());
252 for (Conditional conditional : feature.getConditional()) {
253 for (BundleInfo bundle : conditional.getBundles()) {
254 if (ignoreDependencyFlag || (!ignoreDependencyFlag && !bundle.isDependency())) {
255 resolveArtifact(bundle.getLocation());
259 for (ConfigFile configfile : feature.getConfigfile()) {
260 resolveArtifact(configfile.getLocation());
262 } catch (RuntimeException e) {
263 throw new RuntimeException("Error resolving feature " + feature.getName() + "/" + feature.getVersion(), e);
266 } catch (Exception e) {
267 throw new MojoExecutionException("Error populating repository", e);
272 private Artifact resolveArtifact(String location) throws MojoExecutionException {
273 Artifact artifact = resourceToArtifact(location, skipNonMavenProtocols);
274 if (artifact != null) {
276 resolveArtifact(artifact, remoteRepos);
277 } catch (RuntimeException e) {
278 throw new RuntimeException("Error resolving artifact " + location, e);
281 checkDoGarbageCollect();
286 * Maven ArtifactResolver leaves file handles around so need to clean up
287 * or we will run out of file descriptors
289 protected void checkDoGarbageCollect() {
290 if (this.resolveCount++ % 100 == 0) {
292 System.runFinalization();