Index: command/src/main/java/org/apache/felix/karaf/features/command/completers/AllFeatureCompleter.java =================================================================== --- command/src/main/java/org/apache/felix/karaf/features/command/completers/AllFeatureCompleter.java (revision 0) +++ command/src/main/java/org/apache/felix/karaf/features/command/completers/AllFeatureCompleter.java (revision 0) @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.felix.karaf.features.command.completers; + +import org.apache.felix.karaf.features.Feature; + +/** + * {@link jline.Completor} for all features. + * + * Displays a list of currently installed features. + * + */ +public class AllFeatureCompleter extends FeatureCompleterSupport { + + @Override + protected boolean acceptsFeature(Feature feature) { + return true; + } + +} Index: command/src/main/java/org/apache/felix/karaf/features/command/completers/AvailableFeatureCompleter.java =================================================================== --- command/src/main/java/org/apache/felix/karaf/features/command/completers/AvailableFeatureCompleter.java (revision 917146) +++ command/src/main/java/org/apache/felix/karaf/features/command/completers/AvailableFeatureCompleter.java (working copy) @@ -16,11 +16,6 @@ */ package org.apache.felix.karaf.features.command.completers; -import java.util.List; - -import org.apache.felix.karaf.shell.console.completer.StringsCompleter; -import org.apache.felix.karaf.shell.console.Completer; -import org.apache.felix.karaf.features.FeaturesService; import org.apache.felix.karaf.features.Feature; /** @@ -29,27 +24,11 @@ * Displays a list of available features from installed repositories. * */ -public class AvailableFeatureCompleter implements Completer { +public class AvailableFeatureCompleter extends FeatureCompleterSupport { - private FeaturesService featuresService; - - public void setFeaturesService(FeaturesService featuresService) { - this.featuresService = featuresService; + @Override + protected boolean acceptsFeature(Feature feature) { + return !featuresService.isInstalled(feature); } - public int complete(final String buffer, final int cursor, final List candidates) { - StringsCompleter delegate = new StringsCompleter(); - try { - for (Feature feature : featuresService.listFeatures()) { - if (!featuresService.isInstalled( feature )) { - delegate.getStrings().add(feature.getName()); - } - } - } catch (Exception e) { - // Ignore - } - return delegate.complete(buffer, cursor, candidates); - } - - } Index: command/src/main/java/org/apache/felix/karaf/features/command/completers/FeatureCompleterSupport.java =================================================================== --- command/src/main/java/org/apache/felix/karaf/features/command/completers/FeatureCompleterSupport.java (revision 0) +++ command/src/main/java/org/apache/felix/karaf/features/command/completers/FeatureCompleterSupport.java (revision 0) @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.felix.karaf.features.command.completers; + +import java.util.List; + +import org.apache.felix.karaf.features.Feature; +import org.apache.felix.karaf.features.FeaturesService; +import org.apache.felix.karaf.shell.console.Completer; +import org.apache.felix.karaf.shell.console.completer.StringsCompleter; + +/** + * Base completer for feature commands. + */ +public abstract class FeatureCompleterSupport implements Completer { + + protected FeaturesService featuresService; + + public void setFeaturesService(FeaturesService featuresService) { + this.featuresService = featuresService; + } + + public int complete(final String buffer, final int cursor, final List candidates) { + StringsCompleter delegate = new StringsCompleter(); + try { + for (Feature feature : featuresService.listFeatures()) { + if (acceptsFeature(feature)) { + delegate.getStrings().add(feature.getName()); + } + } + } catch (Exception e) { + // Ignore + } + return delegate.complete(buffer, cursor, candidates); + } + + /** + * Method for filtering features. + * + * @param feature The feature. + * @return True if feature should be available in completer. + */ + protected abstract boolean acceptsFeature(Feature feature); +} Index: command/src/main/java/org/apache/felix/karaf/features/command/completers/InstalledFeatureCompleter.java =================================================================== --- command/src/main/java/org/apache/felix/karaf/features/command/completers/InstalledFeatureCompleter.java (revision 917146) +++ command/src/main/java/org/apache/felix/karaf/features/command/completers/InstalledFeatureCompleter.java (working copy) @@ -16,11 +16,6 @@ */ package org.apache.felix.karaf.features.command.completers; -import java.util.List; - -import org.apache.felix.karaf.shell.console.Completer; -import org.apache.felix.karaf.shell.console.completer.StringsCompleter; -import org.apache.felix.karaf.features.FeaturesService; import org.apache.felix.karaf.features.Feature; /** @@ -29,24 +24,11 @@ * Displays a list of currently installed features. * */ -public class InstalledFeatureCompleter implements Completer { +public class InstalledFeatureCompleter extends FeatureCompleterSupport { - private FeaturesService featuresService; - - public void setFeaturesService(FeaturesService featuresService) { - this.featuresService = featuresService; + @Override + protected boolean acceptsFeature(Feature feature) { + return featuresService.isInstalled(feature); } - public int complete(final String buffer, final int cursor, final List candidates) { - StringsCompleter delegate = new StringsCompleter(); - try { - for (Feature feature : featuresService.listInstalledFeatures()) { - delegate.getStrings().add(feature.getName()); - } - } catch (Exception e) { - // Ignore - } - return delegate.complete(buffer, cursor, candidates); - } - } Index: command/src/main/java/org/apache/felix/karaf/features/command/DescribeFeatureCommand.java =================================================================== --- command/src/main/java/org/apache/felix/karaf/features/command/DescribeFeatureCommand.java (revision 0) +++ command/src/main/java/org/apache/felix/karaf/features/command/DescribeFeatureCommand.java (revision 0) @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.felix.karaf.features.command; + +import java.util.List; +import java.util.Map; + +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Command; +import org.apache.felix.gogo.commands.Option; +import org.apache.felix.karaf.features.Feature; +import org.apache.felix.karaf.features.FeaturesService; + +@Command(scope = "features", name = "desc", description = "Shows information about selected information.") +public class DescribeFeatureCommand extends FeaturesCommandSupport { + + @Argument(index = 0, name = "name", description = "The name of the feature", required = true, multiValued = false) + private String name; + + @Argument(index = 1, name = "version", description = "The version of the feature", required = false, multiValued = false) + private String version; + + @Option(name = "-c", aliases={"--configuration"}, description="Display configuration info", required = false, multiValued = false) + private boolean config; + + @Option(name = "-d", aliases={"--dependency"}, description="Display dependencies info", required = false, multiValued = false) + private boolean dependency; + + @Option(name = "-b", aliases={"--bundle"}, description="Display bundles info", required = false, multiValued = false) + private boolean bundle; + + protected void doExecute(FeaturesService admin) throws Exception { + Feature feature = null; + + if (version != null && version.length() > 0) { + feature = admin.getFeature(name, version); + } else { + feature = admin.getFeature(name); + } + + if (feature == null) { + System.out.println("Feature not found"); + return; + } + + // default behaviour + if (!config && !dependency && !bundle) { + config = true; + dependency = true; + bundle = true; + } + + System.out.println("Description of " + feature.getName() + " " + feature.getVersion() + " feature"); + System.out.println("----------------------------------------------------------------"); + if (config) { + displayConfigInformation(feature); + } + + if (dependency) { + displayDependencyInformation(feature); + } + + if (bundle) { + displayBundleInformation(feature); + } + } + + private void displayBundleInformation(Feature feature) { + List bundles = feature.getBundles(); + if (bundles.isEmpty()) { + System.out.println("Feature has no bundles."); + } else { + System.out.println("Feature contains followed bundles:"); + for (String featureBundle : bundles) { + System.out.println(" " + featureBundle); + } + } + } + + private void displayDependencyInformation(Feature feature) { + List dependencies = feature.getDependencies(); + if (dependencies.isEmpty()) { + System.out.println("Feature has no dependencies."); + } else { + System.out.println("Feature depends on:"); + for (Feature featureDependency : dependencies) { + System.out.println(" " + featureDependency.getName() + " " + featureDependency.getVersion()); + } + } + } + + private void displayConfigInformation(Feature feature) { + Map> configurations = feature.getConfigurations(); + if (configurations.isEmpty()) { + System.out.println("Feature has no configuration"); + } else { + System.out.println("Feature configuration:"); + for (String name : configurations.keySet()) { + System.out.println(" " + name); + } + } + } + +} Index: command/src/main/java/org/apache/felix/karaf/features/command/TreeFeatureCommand.java =================================================================== --- command/src/main/java/org/apache/felix/karaf/features/command/TreeFeatureCommand.java (revision 0) +++ command/src/main/java/org/apache/felix/karaf/features/command/TreeFeatureCommand.java (revision 0) @@ -0,0 +1,88 @@ +package org.apache.felix.karaf.features.command; + +import java.util.List; + +import org.apache.felix.gogo.commands.Argument; +import org.apache.felix.gogo.commands.Option; +import org.apache.felix.karaf.features.Feature; +import org.apache.felix.karaf.features.FeaturesService; + +public class TreeFeatureCommand extends FeaturesCommandSupport { + + @Argument(index = 0, name = "name", description = "The name of the feature", required = true, multiValued = false) + private String name; + + @Argument(index = 1, name = "version", description = "The version of the feature", required = false, multiValued = false) + private String version; + + @Option(name = "-b", aliases={"--bundle"}, description="Add bundles into tree", required = false, multiValued = false) + private boolean bundle; + + @Override + protected void doExecute(FeaturesService admin) throws Exception { + Feature feature = null; + + if (version != null && version.length() > 0) { + feature = admin.getFeature(name, version); + } else { + feature = admin.getFeature(name); + } + + if (feature == null) { + System.out.println("Feature not found"); + return; + } + + int unresolved = displayFeatureTree(admin, feature, 0, false); + if (unresolved > 0) { + System.out.println("Tree contains " + unresolved + " unresolved dependencies"); + System.out.println(" * means that node declares dependency but the dependant feature is not available."); + } + } + + private int displayFeatureTree(FeaturesService admin, Feature feature, int level, boolean last) throws Exception { + int unresolved = 0; + String prefix = repeat(" ", level); + + Feature resolved = resolveFeature(admin, feature); + if (resolved != null) { + System.out.println(prefix + " " + resolved.getName() + " " + resolved.getVersion()); + } else { + System.out.println(prefix + " " + feature.getName() + " " + feature.getVersion() + " *"); + unresolved++; + } + + if (bundle) { + List bundles = resolved != null ? resolved.getBundles() : feature.getBundles(); + for (int i = 0, j = bundles.size(); i < j; i++) { + System.out.println(prefix + " " + (i+1 == j ? "\\" : "+") + " " + bundles.get(i)); + } + } + List dependencies = resolved != null ? resolved.getDependencies() : feature.getDependencies(); + for (int i = 0, j = dependencies.size(); i < j; i++) { + Feature toDisplay = resolveFeature(admin, dependencies.get(i)); + if (toDisplay == null) { + toDisplay = dependencies.get(i); + } + unresolved += displayFeatureTree(admin, toDisplay, level+1, i + 1 == j); + } + + return unresolved; + } + + private Feature resolveFeature(FeaturesService admin, Feature feature) throws Exception { + return admin.getFeature(feature.getName(), feature.getVersion()); + } + + private static String repeat(String string, int times) { + if (times <= 0) { + return ""; + } + else if (times % 2 == 0) { + return repeat(string+string, times/2); + } + else { + return string + repeat(string+string, times/2); + } + } +} Index: command/src/main/resources/OSGI-INF/blueprint/features-command.xml =================================================================== --- command/src/main/resources/OSGI-INF/blueprint/features-command.xml (revision 917146) +++ command/src/main/resources/OSGI-INF/blueprint/features-command.xml (working copy) @@ -51,6 +51,18 @@ + + + + + + + + + + + + @@ -67,4 +79,8 @@ + + + + Index: core/src/main/java/org/apache/felix/karaf/features/FeaturesService.java =================================================================== --- core/src/main/java/org/apache/felix/karaf/features/FeaturesService.java (revision 917146) +++ core/src/main/java/org/apache/felix/karaf/features/FeaturesService.java (working copy) @@ -56,6 +56,10 @@ Feature[] listInstalledFeatures(); + Feature getFeature(String name) throws Exception; + + Feature getFeature(String name, String version) throws Exception; + boolean isInstalled(Feature f); } Index: core/src/main/java/org/apache/felix/karaf/features/internal/FeaturesServiceImpl.java =================================================================== --- core/src/main/java/org/apache/felix/karaf/features/internal/FeaturesServiceImpl.java (revision 917146) +++ core/src/main/java/org/apache/felix/karaf/features/internal/FeaturesServiceImpl.java (working copy) @@ -16,6 +16,8 @@ */ package org.apache.felix.karaf.features.internal; +import static java.lang.String.format; + import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -40,11 +42,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.felix.karaf.features.Feature; +import org.apache.felix.karaf.features.FeatureEvent; +import org.apache.felix.karaf.features.FeaturesListener; import org.apache.felix.karaf.features.FeaturesService; -import org.apache.felix.karaf.features.Feature; import org.apache.felix.karaf.features.Repository; -import org.apache.felix.karaf.features.FeaturesListener; -import org.apache.felix.karaf.features.FeatureEvent; import org.apache.felix.karaf.features.RepositoryEvent; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -54,17 +56,15 @@ import org.osgi.framework.Version; import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.packageadmin.PackageAdmin; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; import org.osgi.service.prefs.PreferencesService; -import org.osgi.service.packageadmin.PackageAdmin; import org.osgi.service.startlevel.StartLevel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; -import static java.lang.String.format; - /** * The Features service implementation. * Adding a repository url will load the features contained in this repository and @@ -579,7 +579,11 @@ return installed.containsKey(f); } - protected Feature getFeature(String name, String version) throws Exception { + public Feature getFeature(String name) throws Exception { + return getFeature(name, FeatureImpl.DEFAULT_VERSION); + } + + public Feature getFeature(String name, String version) throws Exception { if (version != null) { version = version.trim(); }