http://git-wip-us.apache.org/repos/asf/karaf/blob/f13d140c/kar/src/main/java/org/apache/karaf/kar/command/UninstallKarCommand.java ---------------------------------------------------------------------- diff --git a/kar/src/main/java/org/apache/karaf/kar/command/UninstallKarCommand.java b/kar/src/main/java/org/apache/karaf/kar/command/UninstallKarCommand.java new file mode 100644 index 0000000..506bdb3 --- /dev/null +++ b/kar/src/main/java/org/apache/karaf/kar/command/UninstallKarCommand.java @@ -0,0 +1,45 @@ +/* + * 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.karaf.kar.command; + +import org.apache.karaf.kar.KarService; +import org.apache.karaf.kar.command.completers.KarCompleter; +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.Completion; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Command(scope = "kar", name = "uninstall", description = "Uninstall a KAR file.") +@Service +public class UninstallKarCommand implements Action { + + @Argument(index = 0, name = "name", description = "The name of the KAR file to uninstall.", required = true, multiValued = false) + @Completion(KarCompleter.class) + private String name; + + @Reference + private KarService karService; + + @Override + public Object execute() throws Exception { + karService.uninstall(name); + return null; + } + +}
http://git-wip-us.apache.org/repos/asf/karaf/blob/f13d140c/kar/src/main/java/org/apache/karaf/kar/command/completers/KarCompleter.java ---------------------------------------------------------------------- diff --git a/kar/src/main/java/org/apache/karaf/kar/command/completers/KarCompleter.java b/kar/src/main/java/org/apache/karaf/kar/command/completers/KarCompleter.java new file mode 100644 index 0000000..539cfaf --- /dev/null +++ b/kar/src/main/java/org/apache/karaf/kar/command/completers/KarCompleter.java @@ -0,0 +1,50 @@ +/* + * 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.karaf.kar.command.completers; + +import java.util.List; + +import org.apache.karaf.kar.KarService; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; +import org.apache.karaf.shell.api.console.CommandLine; +import org.apache.karaf.shell.api.console.Completer; +import org.apache.karaf.shell.api.console.Session; +import org.apache.karaf.shell.support.completers.StringsCompleter; + +/** + * Completer on all installed KAR files. + */ +@Service +public class KarCompleter implements Completer { + + @Reference + private KarService karService; + + public int complete(Session session, CommandLine commandLine, List<String> candidates) { + StringsCompleter delegate = new StringsCompleter(); + try { + for (String karName : karService.list()) { + delegate.getStrings().add(karName); + } + } catch (Exception e) { + // ignore + } + return delegate.complete(session, commandLine, candidates); + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/f13d140c/kar/src/main/java/org/apache/karaf/kar/internal/FeatureDetector.java ---------------------------------------------------------------------- diff --git a/kar/src/main/java/org/apache/karaf/kar/internal/FeatureDetector.java b/kar/src/main/java/org/apache/karaf/kar/internal/FeatureDetector.java new file mode 100644 index 0000000..aca1f53 --- /dev/null +++ b/kar/src/main/java/org/apache/karaf/kar/internal/FeatureDetector.java @@ -0,0 +1,87 @@ +/* + * 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.karaf.kar.internal; + +import java.io.File; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +/** + * Simple helper to determine if a file is a feature repo + */ +class FeatureDetector { + + private static final Logger LOGGER = LoggerFactory.getLogger(FeatureDetector.class); + + private DocumentBuilderFactory dbf; + + FeatureDetector() { + dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + } + /** + * Check if a file is a features XML. + * + * @param artifact the file to check. + * @return true if the artifact is a features XML, false else. + */ + boolean isFeaturesRepository(File artifact) { + try { + if (artifact.isFile() && artifact.getName().endsWith(".xml")) { + Document doc = parse(artifact); + String name = doc.getDocumentElement().getLocalName(); + String uri = doc.getDocumentElement().getNamespaceURI(); + if ("features".equals(name) && (uri == null || "".equals(uri) || uri.startsWith("http://karaf.apache.org/xmlns/features/v"))) { + return true; + } + } + } catch (Exception e) { + LOGGER.debug("File '{}' is not a features file.", artifact.getName(), e); + } + return false; + } + + /** + * Parse a features XML. + * + * @param artifact the features XML to parse. + * @return the parsed document. + * @throws Exception in case of parsing failure. + */ + private Document parse(File artifact) throws Exception { + DocumentBuilder db = dbf.newDocumentBuilder(); + db.setErrorHandler(new ErrorHandler() { + public void warning(SAXParseException exception) throws SAXException { + } + public void error(SAXParseException exception) throws SAXException { + } + public void fatalError(SAXParseException exception) throws SAXException { + throw exception; + } + }); + return db.parse(artifact); + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/f13d140c/kar/src/main/java/org/apache/karaf/kar/internal/Kar.java ---------------------------------------------------------------------- diff --git a/kar/src/main/java/org/apache/karaf/kar/internal/Kar.java b/kar/src/main/java/org/apache/karaf/kar/internal/Kar.java new file mode 100644 index 0000000..005cd17 --- /dev/null +++ b/kar/src/main/java/org/apache/karaf/kar/internal/Kar.java @@ -0,0 +1,203 @@ +/* + * 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.karaf.kar.internal; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Representation of a Karaf Kar archive + * + * A Kar archive is a jar file with a special structure that can be used + * to deploy feature repositories, maven repo contents and resources for the + * karaf installation. + * + * meta-inf/Manifest: + * Karaf-Feature-Start: (true|false) Controls if the features in the feature repos should be started on deploy + * Karaf-Feature-Repos: (uri)* If present then only the given feature repo urls are added to karaf if it is not + * present then the karaf file is scanned for repo files + * + * repository/ + * Everything below this directory is treated as a maven repository. On deploy the contents + * will be copied to a directory below data. This directory will then be added to the + * maven repos of pax url maven + * + * resource/ + * Everything below this directory will be copied to the karaf base dir on deploy + * + */ +public class Kar { + + public static final Logger LOGGER = LoggerFactory.getLogger(KarServiceImpl.class); + public static final String MANIFEST_ATTR_KARAF_FEATURE_START = "Karaf-Feature-Start"; + public static final String MANIFEST_ATTR_KARAF_FEATURE_REPOS = "Karaf-Feature-Repos"; + private final URI karUri; + private boolean shouldInstallFeatures; + private List<URI> featureRepos; + + public Kar(URI karUri) { + this.karUri = karUri; + } + + /** + * Extract a kar from a given URI into a repository dir and resource dir + * and populate shouldInstallFeatures and featureRepos + * + * @param repoDir directory to write the repository contents of the kar to + * @param resourceDir directory to write the resource contents of the kar to + */ + public void extract(File repoDir, File resourceDir) { + InputStream is = null; + JarInputStream zipIs = null; + FeatureDetector featureDetector = new FeatureDetector(); + this.featureRepos = new ArrayList<URI>(); + this.shouldInstallFeatures = true; + + try { + is = karUri.toURL().openStream(); + repoDir.mkdirs(); + + if (!repoDir.isDirectory()) { + throw new RuntimeException("The KAR file " + karUri + " is already installed"); + } + + LOGGER.debug("Uncompress the KAR file {} into directory {}", karUri, repoDir); + zipIs = new JarInputStream(is); + boolean scanForRepos = true; + + Manifest manifest = zipIs.getManifest(); + if (manifest != null) { + Attributes attr = manifest.getMainAttributes(); + String featureStartSt = (String)attr + .get(new Attributes.Name(MANIFEST_ATTR_KARAF_FEATURE_START)); + if ("false".equals(featureStartSt)) { + shouldInstallFeatures = false; + } + String featureReposAttr = (String)attr + .get(new Attributes.Name(MANIFEST_ATTR_KARAF_FEATURE_REPOS)); + if (featureReposAttr != null) { + featureRepos.add(new URI(featureReposAttr)); + scanForRepos = false; + } + } + + ZipEntry entry = zipIs.getNextEntry(); + while (entry != null) { + if (entry.getName().startsWith("repository")) { + String path = entry.getName().substring("repository/".length()); + File destFile = new File(repoDir, path); + extract(zipIs, entry, destFile); + if (scanForRepos && featureDetector.isFeaturesRepository(destFile)) { + featureRepos.add(destFile.toURI()); + } + } + + if (entry.getName().startsWith("resource")) { + String path = entry.getName().substring("resource/".length()); + File destFile = new File(resourceDir, path); + extract(zipIs, entry, destFile); + } + entry = zipIs.getNextEntry(); + } + } catch (Exception e) { + throw new RuntimeException("Error extracting kar file " + karUri + " into dir " + repoDir + ": " + e.getMessage(), e); + } finally { + closeStream(zipIs); + closeStream(is); + } + } + + /** + * Extract an entry from a KAR file + * + * @param is + * @param zipEntry + * @param dest + * @return + * @throws Exception + */ + private static File extract(InputStream is, ZipEntry zipEntry, File dest) throws Exception { + if (zipEntry.isDirectory()) { + LOGGER.debug("Creating directory {}", dest.getName()); + dest.mkdirs(); + } else { + dest.getParentFile().mkdirs(); + FileOutputStream out = new FileOutputStream(dest); + copyStream(is, out); + out.close(); + } + return dest; + } + + private static void closeStream(InputStream is) { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + LOGGER.warn("Error closing stream", e); + } + } + } + + static long copyStream(InputStream input, OutputStream output) throws IOException { + byte[] buffer = new byte[10000]; + long count = 0; + int n = 0; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + public String getKarName() { + try { + String karName = new File(karUri.toURL().getFile()).getName(); + karName = karName.substring(0, karName.lastIndexOf(".")); + return karName; + } catch (MalformedURLException e) { + throw new RuntimeException("Invalid kar URI " + karUri, e); + } + } + + public URI getKarUri() { + return karUri; + } + + public boolean isShouldInstallFeatures() { + return shouldInstallFeatures; + } + + public List<URI> getFeatureRepos() { + return featureRepos; + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/f13d140c/kar/src/main/java/org/apache/karaf/kar/internal/KarServiceImpl.java ---------------------------------------------------------------------- diff --git a/kar/src/main/java/org/apache/karaf/kar/internal/KarServiceImpl.java b/kar/src/main/java/org/apache/karaf/kar/internal/KarServiceImpl.java new file mode 100644 index 0000000..0f86466 --- /dev/null +++ b/kar/src/main/java/org/apache/karaf/kar/internal/KarServiceImpl.java @@ -0,0 +1,382 @@ +/* + * 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.karaf.kar.internal; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +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.kar.KarService; +import org.apache.karaf.util.maven.Parser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Implementation of the KAR service. + */ +public class KarServiceImpl implements KarService { + private static final String FEATURE_CONFIG_FILE = "features.cfg"; + private static final Logger LOGGER = LoggerFactory.getLogger(KarServiceImpl.class); + + private File storage; + private File base; + private FeaturesService featuresService; + + private boolean noAutoRefreshBundles; + + public KarServiceImpl(String karafBase, FeaturesService featuresService) { + this.base = new File(karafBase); + this.storage = new File(this.base, "data" + File.separator + "kar"); + this.featuresService = featuresService; + this.storage.mkdirs(); + if (!storage.isDirectory()) { + throw new IllegalStateException("KAR storage " + storage + " is not a directory"); + } + } + + @Override + public void install(URI karUri) throws Exception { + String karName = new Kar(karUri).getKarName(); + LOGGER.debug("Installing KAR {} from {}", karName, karUri); + File karDir = new File(storage, karName); + install(karUri, karDir, base); + } + + @Override + public void install(URI karUri, File repoDir, File resourceDir) throws Exception { + Kar kar = new Kar(karUri); + kar.extract(repoDir, resourceDir); + writeToFile(kar.getFeatureRepos(), new File(repoDir, FEATURE_CONFIG_FILE)); + for (URI uri : kar.getFeatureRepos()) { + addToFeaturesRepositories(uri); + } + if (kar.isShouldInstallFeatures()) { + installFeatures(kar.getFeatureRepos()); + } + + } + + + private List<URI> readFromFile(File repoListFile) { + ArrayList<URI> uriList = new ArrayList<URI>(); + FileReader fr = null; + try { + fr = new FileReader(repoListFile); + BufferedReader br = new BufferedReader(fr); + String line; + while ((line = br.readLine()) != null) { + uriList.add(new URI(line)); + } + } catch (Exception e) { + throw new RuntimeException("Error reading repo list from file " + repoListFile.getAbsolutePath(), e); + } finally { + try { + fr.close(); + } catch (IOException e) { + LOGGER.warn("Error closing reader for file " + repoListFile, e); + } + } + return uriList; + } + + private void writeToFile(List<URI> featuresRepositoriesInKar, File repoListFile) { + FileOutputStream fos = null; + PrintStream ps = null; + try { + fos = new FileOutputStream(repoListFile); + ps = new PrintStream(fos); + for (URI uri : featuresRepositoriesInKar) { + ps.println(uri); + } + ps.close(); + fos.close(); + } catch (Exception e) { + throw new RuntimeException("Error writing feature repo list to file " + repoListFile.getAbsolutePath(), e); + } finally { + closeStream(ps); + closeStream(fos); + } + } + + private void deleteRecursively(File dir) { + if (dir.isDirectory()) { + File[] children = dir.listFiles(); + for (File child : children) { + deleteRecursively(child); + } + } + dir.delete(); + } + + @Override + public void uninstall(String karName) throws Exception { + File karDir = new File(storage, karName); + + if (!karDir.exists()) { + throw new IllegalArgumentException("The KAR " + karName + " is not installed"); + } + + List<URI> featuresRepositories = readFromFile(new File(karDir, FEATURE_CONFIG_FILE)); + uninstallFeatures(featuresRepositories); + for (URI featuresRepository : featuresRepositories) { + featuresService.removeRepository(featuresRepository); + } + + deleteRecursively(karDir); + } + + @Override + public List<String> list() throws Exception { + List<String> kars = new ArrayList<String>(); + for (File kar : storage.listFiles()) { + if (kar.isDirectory()) { + kars.add(kar.getName()); + } + } + return kars; + } + + + + /** + * Add an URI to the list of features repositories. + * + * @param uri the URI to add. + * @throws Exception in case of add failure. + */ + private void addToFeaturesRepositories(URI uri) throws Exception { + try { + featuresService.removeRepository(uri); + featuresService.addRepository(uri); + LOGGER.info("Added feature repository '{}'", uri); + } catch (Exception e) { + LOGGER.warn("Unable to add repository '{}'", uri, e); + } + } + + /** + * Install all features contained in the list of features XML. + * + * @param featuresRepositories the list of features XML. + */ + private void installFeatures(List<URI> featuresRepositories) throws Exception { + for (Repository repository : featuresService.listRepositories()) { + for (URI karFeatureRepoUri : featuresRepositories) { + if (repository.getURI().equals(karFeatureRepoUri)) { + try { + for (Feature feature : repository.getFeatures()) { + try { + LOGGER.debug("noAutoRefreshBundles is " + isNoAutoRefreshBundles()); + if (isNoAutoRefreshBundles()) { + featuresService.installFeature(feature, EnumSet.of(FeaturesService.Option.NoAutoRefreshBundles)); + } else { + featuresService.installFeature(feature, EnumSet.noneOf(FeaturesService.Option.class)); + } + } catch (Exception e) { + LOGGER.warn("Unable to install Kar feature {}", feature.getName() + "/" + feature.getVersion(), e); + } + } + } catch (Exception e) { + LOGGER.warn("Can't get features for KAR {}", karFeatureRepoUri, e); + } + } + } + } + } + + @Override + public void create(String repoName, List<String> features, PrintStream console) { + FileOutputStream fos = null; + JarOutputStream jos = null; + try { + Repository repo = featuresService.getRepository(repoName); + if (repo == null) { + throw new RuntimeException("Could not find a repository with name " + repoName); + } + String karPath = storage + File.separator + repoName + ".kar"; + File karFile = new File(karPath); + karFile.getParentFile().mkdirs(); + fos = new FileOutputStream(karFile); + Manifest manifest = createNonAutoStartManifest(repo.getURI()); + jos = new JarOutputStream(new BufferedOutputStream(fos, 100000), manifest); + + Map<URI, Integer> locationMap = new HashMap<URI, Integer>(); + copyResourceToJar(jos, repo.getURI(), locationMap); + + Map<String, Feature> featureMap = new HashMap<String, Feature>(); + for (Feature feature : repo.getFeatures()) { + featureMap.put(feature.getName(), feature); + } + + Set<Feature> featuresToCopy = getFeatures(featureMap, features, 1); + + for (Feature feature : featuresToCopy) { + if (console != null) + console.println("Adding feature " + feature.getName()); + copyFeatureToJar(jos, feature, locationMap); + } + + if (console != null) + console.println("Kar file created : " + karPath); + } catch (Exception e) { + throw new RuntimeException("Error creating kar: " + e.getMessage(), e); + } finally { + closeStream(jos); + closeStream(fos); + } + + } + + private Set<Feature> getFeatures(Map<String, Feature> featureMap, List<String> features, int depth) { + Set<Feature> featureSet = new HashSet<Feature>(); + if (depth > 5) { + // Break after some recursions to avoid endless loops + return featureSet; + } + if (features == null) { + featureSet.addAll(featureMap.values()); + return featureSet; + } + for (String featureName : features) { + Feature feature = featureMap.get(featureName); + if (feature == null) { + System.out.println("Feature " + featureName + " not found in repository."); + //throw new RuntimeException(); + } else { + featureSet.add(feature); + List<Dependency> deps = feature.getDependencies(); + List<String> depNames = new ArrayList<String>(); + for (Dependency dependency : deps) { + depNames.add(dependency.getName()); + } + featureSet.addAll(getFeatures(featureMap, depNames, depth ++)); + } + } + return featureSet; + } + + private Manifest createNonAutoStartManifest(URI repoUri) throws UnsupportedEncodingException, IOException { + String manifestSt = "Manifest-Version: 1.0\n" + + Kar.MANIFEST_ATTR_KARAF_FEATURE_START +": false\n" + + Kar.MANIFEST_ATTR_KARAF_FEATURE_REPOS + ": " + repoUri.toString() + "\n"; + InputStream manifestIs = new ByteArrayInputStream(manifestSt.getBytes("UTF-8")); + Manifest manifest = new Manifest(manifestIs); + return manifest; + } + + private void closeStream(OutputStream os) { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + LOGGER.warn("Error closing stream", e); + } + } + } + + private void copyFeatureToJar(JarOutputStream jos, Feature feature, Map<URI, Integer> locationMap) + throws URISyntaxException { + for (BundleInfo bundleInfo : feature.getBundles()) { + URI location = new URI(bundleInfo.getLocation()); + copyResourceToJar(jos, location, locationMap); + } + for (ConfigFileInfo configFileInfo : feature.getConfigurationFiles()) { + URI location = new URI(configFileInfo.getLocation()); + copyResourceToJar(jos, location, locationMap); + } + } + + private void copyResourceToJar(JarOutputStream jos, URI location, Map<URI, Integer> locationMap) { + if (locationMap.containsKey(location)) { + return; + } + try { + String noPrefixLocation = location.toString().substring(location.toString().lastIndexOf(":") + 1); + Parser parser = new Parser(noPrefixLocation); + InputStream is = location.toURL().openStream(); + String path = "repository/" + parser.getArtifactPath(); + jos.putNextEntry(new JarEntry(path)); + Kar.copyStream(is, jos); + is.close(); + locationMap.put(location, 1); + } catch (Exception e) { + LOGGER.error("Error adding " + location, e); + } + } + + /** + * Uninstall all features contained in the list of features XML. + * + * @param featuresRepositories the list of features XML. + */ + private void uninstallFeatures(List<URI> featuresRepositories) throws Exception { + for (Repository repository : featuresService.listRepositories()) { + for (URI karFeatureRepoUri : featuresRepositories) { + if (repository.getURI().equals(karFeatureRepoUri)) { + try { + for (Feature feature : repository.getFeatures()) { + try { + featuresService.uninstallFeature(feature.getName(), feature.getVersion()); + } catch (Exception e) { + LOGGER.warn("Unable to uninstall Kar feature {}", feature.getName() + "/" + feature.getVersion(), e); + } + } + } catch (Exception e) { + LOGGER.warn("Can't get features for KAR {}", karFeatureRepoUri, e); + } + } + } + } + } + + public boolean isNoAutoRefreshBundles() { + return noAutoRefreshBundles; + } + + public void setNoAutoRefreshBundles(boolean noAutoRefreshBundles) { + this.noAutoRefreshBundles = noAutoRefreshBundles; + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/f13d140c/kar/src/main/java/org/apache/karaf/kar/internal/KarsMBeanImpl.java ---------------------------------------------------------------------- diff --git a/kar/src/main/java/org/apache/karaf/kar/internal/KarsMBeanImpl.java b/kar/src/main/java/org/apache/karaf/kar/internal/KarsMBeanImpl.java new file mode 100644 index 0000000..b274e21 --- /dev/null +++ b/kar/src/main/java/org/apache/karaf/kar/internal/KarsMBeanImpl.java @@ -0,0 +1,72 @@ +/* + * 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.karaf.kar.internal; + +import org.apache.karaf.kar.KarService; +import org.apache.karaf.kar.KarsMBean; + +import javax.management.MBeanException; +import javax.management.NotCompliantMBeanException; +import javax.management.StandardMBean; +import java.net.URI; +import java.util.List; + +public class KarsMBeanImpl extends StandardMBean implements KarsMBean { + + private KarService karService; + + public KarsMBeanImpl() throws NotCompliantMBeanException { + super(KarsMBean.class); + } + + public List<String> getKars() throws MBeanException { + try { + return karService.list(); + } catch (Exception e) { + throw new MBeanException(null, e.getMessage()); + } + } + + public void create(String repoName, List<String> features) { + karService.create(repoName, features, null); + } + + public void install(String url) throws MBeanException { + try { + karService.install(new URI(url)); + } catch (Exception e) { + throw new MBeanException(null, e.getMessage()); + } + } + + public void uninstall(String name) throws MBeanException { + try { + karService.uninstall(name); + } catch (Exception e) { + throw new MBeanException(null, e.getMessage()); + } + } + + public KarService getKarService() { + return karService; + } + + public void setKarService(KarService karService) { + this.karService = karService; + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/f13d140c/kar/src/main/java/org/apache/karaf/kar/internal/osgi/Activator.java ---------------------------------------------------------------------- diff --git a/kar/src/main/java/org/apache/karaf/kar/internal/osgi/Activator.java b/kar/src/main/java/org/apache/karaf/kar/internal/osgi/Activator.java new file mode 100644 index 0000000..05a1b59 --- /dev/null +++ b/kar/src/main/java/org/apache/karaf/kar/internal/osgi/Activator.java @@ -0,0 +1,54 @@ +/* + * 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.karaf.kar.internal.osgi; + +import org.apache.karaf.features.FeaturesService; +import org.apache.karaf.kar.KarService; +import org.apache.karaf.kar.internal.KarServiceImpl; +import org.apache.karaf.kar.internal.KarsMBeanImpl; +import org.apache.karaf.util.tracker.BaseActivator; +import org.osgi.service.cm.ManagedService; + +public class Activator extends BaseActivator implements ManagedService { + + @Override + protected void doOpen() throws Exception { + manage("org.apache.karaf.kar"); + trackService(FeaturesService.class); + } + + protected void doStart() throws Exception { + FeaturesService featuresService = getTrackedService(FeaturesService.class); + if (featuresService == null) { + return; + } + + boolean noAutoRefreshBundles = getBoolean("noAutoRefreshBundles", false); + + KarServiceImpl karService = new KarServiceImpl( + System.getProperty("karaf.base"), + featuresService + ); + karService.setNoAutoRefreshBundles(noAutoRefreshBundles); + register(KarService.class, karService); + + KarsMBeanImpl mbean = new KarsMBeanImpl(); + mbean.setKarService(karService); + registerMBean(mbean, "type=kar"); + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/f13d140c/kar/src/main/resources/OSGI-INF/bundle.info ---------------------------------------------------------------------- diff --git a/kar/src/main/resources/OSGI-INF/bundle.info b/kar/src/main/resources/OSGI-INF/bundle.info new file mode 100644 index 0000000..7056453 --- /dev/null +++ b/kar/src/main/resources/OSGI-INF/bundle.info @@ -0,0 +1,19 @@ +h1. Synopsis + +${project.name} + +${project.description} + +Maven URL: +[mvn:${project.groupId}/${project.artifactId}/${project.version}] + +h1. Description + +This bundle is the core implementation of the Karaf kar support. + +Karaf Archives (KAR) is an artifact (zip file) shipping a features XML and the associated bundles or configuration +files. + +h1. See also + +KAR - section of the Karaf User Guide