/*
* Copyright 2012 The Solmix Project
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.gnu.org/licenses/
* or see the FSF site: http://www.fsf.org.
*/
package org.solmix.command.karaf;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.karaf.features.BundleInfo;
import org.apache.karaf.features.ConfigFileInfo;
import org.apache.karaf.features.Dependency;
import org.apache.karaf.features.Feature;
import org.apache.karaf.features.FeaturesService;
import org.apache.karaf.features.Repository;
import org.apache.karaf.features.Resolver;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author solmix.f@gmail.com
* @version $Id$ 2013-12-12
*/
@Command(scope = "download", name = "feature", description = "downlaod the karaf features relative archives")
public class FeatureDowndloadCommand extends AbstractDownloadCommand
{
private static final Logger LOGGER = LoggerFactory.getLogger(FeatureDowndloadCommand.class);
@Argument(index = 0, name = "feature", description = "The name and version of the features to install. A feature id looks like name/version. The version is optional.", required = true, multiValued = true)
List<String> features;
private Map<String, Map<String, Feature>> allFeatures;
private FeaturesService service;
@Override
protected Object doExecute() throws Exception {
ServiceReference<?> ref = getBundleContext().getServiceReference(FeaturesService.class.getName());
if (ref == null) {
System.out.println("FeaturesService service is unavailable.");
return null;
}
try {
FeaturesService admin = (FeaturesService) getBundleContext().getService(ref);
if (admin == null) {
System.out.println("FeaturesService service is unavailable.");
return null;
}
doExecute(admin);
} finally {
getBundleContext().ungetService(ref);
}
return null;
}
/**
* @param admin
* @throws Exception
*/
private void doExecute(FeaturesService admin) throws Exception {
service = admin;
if(features.size()==1&&"*".equals(features.get(0))){
for(String featureName:this.getFeatures().keySet()){
downloadArchive( featureName);
}
}
for (String feature : features) {
String[] split = feature.split("/");
String name = split[0];
downloadArchive( name);
}
}
private void downloadArchive(Feature feature) throws Exception {
for (Dependency dependency : feature.getDependencies()) {
// VersionRange range = "0.0.0".equals(dependency.getVersion()) ? VersionRange.ANY_VERSION : new VersionRange(
// dependency.getVersion(), true, true);
Feature fi = null;
Map<String, Feature> avail = getFeatures().get(dependency.getName());
if (avail != null) {
for (Feature f : avail.values()) {
// Version v = VersionTable.getVersion(f.getVersion());
// if (range.contains(v)) {
// if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) {
fi = f;
// }
// }
}
}
if (fi == null) {
throw new Exception("No feature named '" + dependency.getName() + "' with version '" + dependency.getVersion() + "' available");
}
if (!(fi.getName().equals(feature.getName()) && fi.getVersion().equals(feature.getVersion()))) {
downloadArchive( fi);
}
}
for (ConfigFileInfo configFile : feature.getConfigurationFiles()) {
downLoadConfigurationFile(configFile.getLocation(),
configFile.getFinalname(), configFile.isOverride());
}
for (BundleInfo bInfo : resolve(feature)) {
try {
downLoadBundle(bInfo);
} catch (Exception e) {
System.out.println("\u001B[31m"+e.getMessage()+"\u001B[0m");
LOGGER.error(e.getMessage());
}
}
}
/**
* @param bInfo
* @throws IOException
*/
private void downLoadBundle(BundleInfo bInfo) throws IOException {
String location = bInfo.getLocation();
String finalname = getDownLoadedFile(location);
downLoadFile(location,finalname);
}
/**
* @param location
* @param finalname
* @param override
* @throws IOException
*/
private void downLoadConfigurationFile(String location, String name, boolean override) throws IOException {
String finalname = getDownLoadedFile(location);
downLoadFile(location,finalname);
}
/**
* @param feature
* @return
* @throws Exception
*/
private List<BundleInfo> resolve(Feature feature) throws Exception {
String resolver = feature.getResolver();
// If no resolver is specified, we expect a list of uris
if (resolver == null || resolver.length() == 0) {
return feature.getBundles();
}
boolean optional = false;
if (resolver.startsWith("(") && resolver.endsWith(")")) {
resolver = resolver.substring(1, resolver.length() - 1);
optional = true;
}
// Else, find the resolver
String filter = "(&(" + Constants.OBJECTCLASS + "=" + Resolver.class.getName() + ")(name=" + resolver + "))";
ServiceTracker tracker = new ServiceTracker(bundleContext, FrameworkUtil.createFilter(filter), null);
tracker.open();
try {
if (optional) {
Resolver r = (Resolver) tracker.getService();
if (r != null) {
return r.resolve(feature);
} else {
LOGGER.debug("Optional resolver '" + resolver + "' not found, using the default resolver");
return feature.getBundles();
}
} else {
Resolver r = (Resolver) tracker.waitForService(5000);
if (r == null) {
throw new Exception("Unable to find required resolver '" + resolver + "'");
}
return r.resolve(feature);
}
} finally {
tracker.close();
}
}
/**
* @param name
*/
private void downloadArchive(String name) {
try {
Feature feature = service.getFeature(name);
downloadArchive(feature);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
if (allFeatures == null) {
// the outer map's key is feature name, the inner map's key is feature version
Map<String, Map<String, Feature>> map = new HashMap<String, Map<String, Feature>>();
// Two phase load:
// * first load dependent repositories
Repository[] repos = service.listRepositories();
Map<URI, Repository> repositories = new HashMap<URI, Repository>();
for (Repository repo : repos) {
repositories.put(repo.getURI(), repo);
}
// * then load all features
for (Repository repo : repositories.values()) {
for (Feature f : repo.getFeatures()) {
if (map.get(f.getName()) == null) {
Map<String, Feature> versionMap = new HashMap<String, Feature>();
versionMap.put(f.getVersion(), f);
map.put(f.getName(), versionMap);
} else {
map.get(f.getName()).put(f.getVersion(), f);
}
}
}
allFeatures = map;
}
return allFeatures;
}
}