Skip to content

Commit 6f2a387

Browse files
committed
Add feature:contains command (#2523)
The command lists features that contain a specific bundle
1 parent dcbd467 commit 6f2a387

2 files changed

Lines changed: 116 additions & 0 deletions

File tree

features/command/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@
5454
<groupId>org.osgi</groupId>
5555
<artifactId>org.osgi.framework</artifactId>
5656
</dependency>
57+
<dependency>
58+
<!-- for ContainsBundleCommand -->
59+
<groupId>org.apache.karaf.bundle</groupId>
60+
<artifactId>org.apache.karaf.bundle.core</artifactId>
61+
</dependency>
5762
<dependency>
5863
<groupId>org.apache.karaf.features</groupId>
5964
<artifactId>org.apache.karaf.features.core</artifactId>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.karaf.features.command;
18+
19+
import org.apache.karaf.bundle.core.BundleService;
20+
import org.apache.karaf.features.BundleInfo;
21+
import org.apache.karaf.features.Feature;
22+
import org.apache.karaf.features.FeaturesService;
23+
import org.apache.karaf.features.command.completers.InstalledRepoNameCompleter;
24+
import org.apache.karaf.shell.api.action.Command;
25+
import org.apache.karaf.shell.api.action.Completion;
26+
import org.apache.karaf.shell.api.action.Option;
27+
import org.apache.karaf.shell.api.action.lifecycle.Reference;
28+
import org.apache.karaf.shell.api.action.lifecycle.Service;
29+
import org.apache.karaf.shell.support.CommandException;
30+
import org.osgi.framework.Bundle;
31+
32+
import java.util.Arrays;
33+
import java.util.List;
34+
import java.util.Set;
35+
import java.util.function.Predicate;
36+
import java.util.stream.Collectors;
37+
38+
@Command(scope = "feature", name = "contains", description = "Finds the features that contain a specified bundle.")
39+
@Service
40+
public class ContainsBundleCommand extends FeaturesCommandSupport {
41+
42+
@Option(name = "-b", description = "Show features containing the given bundle id", required = false, multiValued = false)
43+
Integer bundleId;
44+
45+
@Option(name = "-s", description = "Show features containing a bundle with the given symbolic name", required = false, multiValued = false)
46+
String bundleName;
47+
48+
@Option(name = "-u", description = "Show features containing a bundle with the given URL", required = false, multiValued = false)
49+
String bundleUrl;
50+
51+
@Option(name = "-i", aliases = {"--installed"}, description = "Display a list of all installed features only", required = false, multiValued = false)
52+
boolean onlyInstalled;
53+
54+
@Option(name = "--repository", description = "Only list features from that repository", required = false, multiValued = false)
55+
@Completion(InstalledRepoNameCompleter.class)
56+
String repository;
57+
58+
@Reference
59+
BundleService bundleService;
60+
61+
@Override
62+
protected void doExecute(FeaturesService featuresService) throws Exception {
63+
String bundleLocation;
64+
if (bundleId != null) {
65+
Bundle bundle = bundleService.getBundle(bundleId.toString());
66+
if (bundle == null)
67+
throw new CommandException("No bundles found with id " + bundleId);
68+
bundleLocation = bundleService.getInfo(bundle).getUpdateLocation();
69+
} else if (bundleName != null) {
70+
Bundle bundle = bundleService.selectBundles(List.of(), true).stream()
71+
.filter(b -> bundleName.equals(b.getSymbolicName()))
72+
.findAny()
73+
.orElseThrow(() -> new CommandException("No bundles found with name " + bundleName));
74+
bundleLocation = bundleService.getInfo(bundle).getUpdateLocation();
75+
} else if (bundleUrl != null) {
76+
bundleLocation = bundleUrl;
77+
} else {
78+
throw new CommandException("Either bundle id, name or URL must be specified");
79+
}
80+
81+
if (bundleLocation == null)
82+
throw new CommandException("Could not determine URL of the specified bundle");
83+
84+
Feature[] features;
85+
if (repository != null && !onlyInstalled)
86+
features = featuresService.getFeatures(repository);
87+
else
88+
features = featuresService.listFeatures();
89+
90+
Set<Feature> containingFeatures = Arrays.stream(features)
91+
.filter(f -> !onlyInstalled || featuresService.isInstalled(f))
92+
.filter(f -> f.getBundles().stream().anyMatch(byBundleLocation(bundleLocation)))
93+
.collect(Collectors.toUnmodifiableSet());
94+
95+
containingFeatures.stream()
96+
.map(f -> String.format("%s %s", f.getName(), f.getVersion()))
97+
.sorted()
98+
.forEach(System.out::println);
99+
}
100+
101+
private Predicate<BundleInfo> byBundleLocation(String bundleLocation) {
102+
return bundleInfo ->
103+
{
104+
String location = bundleInfo.getLocation();
105+
String originalLocation = bundleInfo.getOriginalLocation();
106+
return (location != null && location.startsWith(bundleLocation)) ||
107+
(originalLocation != null && originalLocation.startsWith(bundleLocation));
108+
};
109+
}
110+
111+
}

0 commit comments

Comments
 (0)