/* * 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.HashMap; import java.util.List; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import org.apache.karaf.util.StreamUtils; import org.apache.karaf.util.maven.Parser; 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)) { Map map = new HashMap<>(); String uri = Parser.pathToMaven(path, map); if (map.get("classifier") != null && ((String) map.get("classifier")).equalsIgnoreCase("features")) featureRepos.add(URI.create(uri)); else featureRepos.add(destFile.toURI()); } } if (entry.getName().startsWith("resources/")) { String path = entry.getName().substring("resources/".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); StreamUtils.copy(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); } } } public String getKarName() { try { String url = karUri.toURL().toString(); if (url.startsWith("mvn")) { int index = url.indexOf("/"); url = url.substring(index + 1); index = url.indexOf("/"); url = url.substring(0, index); return url; } else { 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; } }