| /* |
| * 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.profile.assembly; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.MalformedURLException; |
| import java.net.URI; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.*; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.UUID; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.jar.Attributes; |
| import java.util.jar.Manifest; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipInputStream; |
| |
| import org.apache.felix.resolver.ResolverImpl; |
| import org.apache.felix.utils.manifest.Clause; |
| import org.apache.felix.utils.properties.InterpolationHelper; |
| import org.apache.felix.utils.properties.Properties; |
| import org.apache.felix.utils.version.VersionRange; |
| import org.apache.felix.utils.version.VersionTable; |
| import org.apache.karaf.features.FeaturesService; |
| import org.apache.karaf.features.Library; |
| import org.apache.karaf.features.internal.download.DownloadCallback; |
| import org.apache.karaf.features.internal.download.DownloadManager; |
| import org.apache.karaf.features.internal.download.Downloader; |
| import org.apache.karaf.features.internal.download.StreamProvider; |
| import org.apache.karaf.features.internal.download.impl.DownloadManagerHelper; |
| import org.apache.karaf.features.internal.model.Bundle; |
| import org.apache.karaf.features.internal.model.Conditional; |
| import org.apache.karaf.features.internal.model.ConfigFile; |
| import org.apache.karaf.features.internal.model.Dependency; |
| import org.apache.karaf.features.internal.model.Feature; |
| import org.apache.karaf.features.internal.model.Features; |
| import org.apache.karaf.features.internal.model.JaxbUtil; |
| import org.apache.karaf.features.internal.repository.BaseRepository; |
| import org.apache.karaf.features.internal.resolver.ResourceBuilder; |
| import org.apache.karaf.features.internal.service.Blacklist; |
| import org.apache.karaf.features.internal.service.Deployer; |
| import org.apache.karaf.features.internal.util.MapUtils; |
| import org.apache.karaf.kar.internal.Kar; |
| import org.apache.karaf.profile.Profile; |
| import org.apache.karaf.profile.ProfileBuilder; |
| import org.apache.karaf.profile.impl.Profiles; |
| import org.apache.karaf.tools.utils.KarafPropertiesEditor; |
| import org.apache.karaf.tools.utils.model.KarafPropertyEdit; |
| import org.apache.karaf.tools.utils.model.KarafPropertyEdits; |
| import org.apache.karaf.profile.versioning.VersionUtils; |
| import org.apache.karaf.util.config.PropertiesLoader; |
| import org.apache.karaf.util.maven.Parser; |
| import org.ops4j.pax.url.mvn.MavenResolver; |
| import org.ops4j.pax.url.mvn.MavenResolvers; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.Version; |
| import org.osgi.framework.wiring.BundleRevision; |
| import org.osgi.resource.Resource; |
| import org.osgi.service.resolver.Resolver; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import static java.util.jar.JarFile.MANIFEST_NAME; |
| import static org.apache.karaf.profile.assembly.Builder.Stage.Startup; |
| |
| public class Builder { |
| |
| private static final String STATIC_FEATURES_KAR = "mvn:org.apache.karaf.features/static/%s/kar"; |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(Builder.class); |
| |
| private static final String FEATURES_REPOSITORIES = "featuresRepositories"; |
| private static final String FEATURES_BOOT = "featuresBoot"; |
| |
| private static final String LIBRARY_CLAUSE_TYPE = "type"; |
| private static final String LIBRARY_CLAUSE_EXPORT = "export"; |
| private static final String LIBRARY_CLAUSE_DELEGATE = "delegate"; |
| public static final String ORG_OPS4J_PAX_URL_MVN_PID = "org.ops4j.pax.url.mvn"; |
| |
| public static enum Stage { |
| Startup, Boot, Installed |
| } |
| |
| public static enum KarafVersion { |
| v24, v3x, v4x |
| } |
| |
| |
| public static enum BlacklistPolicy { |
| Discard, |
| Fail |
| } |
| |
| static class RepositoryInfo { |
| Stage stage; |
| boolean addAll; |
| } |
| |
| // |
| // Input parameters |
| // |
| |
| List<String> profilesUris = new ArrayList<>(); |
| boolean defaultAddAll = true; |
| Stage defaultStage = Stage.Startup; |
| Map<String, RepositoryInfo> kars = new LinkedHashMap<>(); |
| Map<String, Stage> profiles = new LinkedHashMap<>(); |
| Map<String, RepositoryInfo> repositories = new LinkedHashMap<>(); |
| Map<String, Stage> features = new LinkedHashMap<>(); |
| Map<String, Stage> bundles = new LinkedHashMap<>(); |
| List<String> blacklistedProfiles = new ArrayList<>(); |
| List<String> blacklistedFeatures = new ArrayList<>(); |
| List<String> blacklistedBundles = new ArrayList<>(); |
| BlacklistPolicy blacklistPolicy = BlacklistPolicy.Discard; |
| List<String> libraries = new ArrayList<>(); |
| String javase = "1.7"; |
| KarafVersion karafVersion = KarafVersion.v4x; |
| String environment = null; |
| boolean useReferenceUrls; |
| boolean ignoreDependencyFlag; |
| int defaultStartLevel = 50; |
| Path homeDirectory; |
| boolean offline; |
| String localRepository; |
| String mavenRepositories; |
| |
| private ScheduledExecutorService executor; |
| private DownloadManager manager; |
| private Resolver resolver; |
| private Path etcDirectory; |
| private Path systemDirectory; |
| private Map<String, Profile> allProfiles; |
| private KarafPropertyEdits propertyEdits; |
| |
| public static Builder newInstance() { |
| return new Builder(); |
| } |
| |
| public Builder defaultStage(Stage stage) { |
| this.defaultStage = stage; |
| return this; |
| } |
| |
| public Builder defaultAddAll(boolean addAll) { |
| this.defaultAddAll = addAll; |
| return this; |
| } |
| |
| public Builder profilesUris(String... profilesUri) { |
| Collections.addAll(this.profilesUris, profilesUri); |
| return this; |
| } |
| |
| public Builder libraries(String... libraries) { |
| Collections.addAll(this.libraries, libraries); |
| return this; |
| } |
| |
| public Builder kars(String... kars) { |
| return kars(defaultStage, defaultAddAll, kars); |
| } |
| |
| public Builder kars(boolean addAll, String... kars) { |
| return kars(defaultStage, addAll, kars); |
| } |
| |
| public Builder kars(Stage stage, boolean addAll, String... kars) { |
| for (String kar : kars) { |
| RepositoryInfo info = new RepositoryInfo(); |
| info.stage = stage; |
| info.addAll = addAll; |
| this.kars.put(kar, info); |
| } |
| return this; |
| } |
| |
| public Builder repositories(String... repositories) { |
| return repositories(defaultStage, defaultAddAll, repositories); |
| } |
| |
| public Builder repositories(boolean addAll, String... repositories) { |
| return repositories(defaultStage, addAll, repositories); |
| } |
| |
| public Builder repositories(Stage stage, boolean addAll, String... repositories) { |
| for (String repository : repositories) { |
| RepositoryInfo info = new RepositoryInfo(); |
| info.stage = stage; |
| info.addAll = addAll; |
| this.repositories.put(repository, info); |
| } |
| return this; |
| } |
| |
| public Builder features(String... features) { |
| return features(defaultStage, features); |
| } |
| |
| public Builder features(Stage stage, String... features) { |
| for (String feature : features) { |
| this.features.put(feature, stage); |
| } |
| return this; |
| } |
| |
| public Builder bundles(String... bundles) { |
| return bundles(defaultStage, bundles); |
| } |
| |
| public Builder bundles(Stage stage, String... bundles) { |
| for (String bundle : bundles) { |
| this.bundles.put(bundle, stage); |
| } |
| return this; |
| } |
| |
| public Builder profiles(String... profiles) { |
| return profiles(defaultStage, profiles); |
| } |
| |
| public Builder profiles(Stage stage, String... profiles) { |
| for (String profile : profiles) { |
| this.profiles.put(profile, stage); |
| } |
| return this; |
| } |
| |
| public Builder homeDirectory(Path homeDirectory) { |
| if (homeDirectory == null) { |
| throw new IllegalArgumentException("homeDirectory is null"); |
| } |
| this.homeDirectory = homeDirectory; |
| return this; |
| } |
| |
| public Builder javase(String javase) { |
| if (javase == null) { |
| throw new IllegalArgumentException("javase is null"); |
| } |
| this.javase = javase; |
| return this; |
| } |
| |
| public Builder environment(String environment) { |
| this.environment = environment; |
| return this; |
| } |
| |
| public Builder useReferenceUrls() { |
| return useReferenceUrls(true); |
| } |
| |
| public Builder useReferenceUrls(boolean useReferenceUrls) { |
| this.useReferenceUrls = useReferenceUrls; |
| return this; |
| } |
| |
| public Builder karafVersion(KarafVersion karafVersion) { |
| this.karafVersion = karafVersion; |
| return this; |
| } |
| |
| public Builder defaultStartLevel(int defaultStartLevel) { |
| this.defaultStartLevel = defaultStartLevel; |
| return this; |
| } |
| |
| public Builder ignoreDependencyFlag() { |
| return ignoreDependencyFlag(true); |
| } |
| |
| public Builder ignoreDependencyFlag(boolean ignoreDependencyFlag) { |
| this.ignoreDependencyFlag = ignoreDependencyFlag; |
| return this; |
| } |
| |
| public Builder offline(boolean offline) { |
| this.offline = offline; |
| return this; |
| } |
| |
| public Builder offline() { |
| return offline(true); |
| } |
| |
| public Builder localRepository(String localRepository) { |
| this.localRepository = localRepository; |
| return this; |
| } |
| |
| public Builder mavenRepositories(String mavenRepositories) { |
| this.mavenRepositories = mavenRepositories; |
| return this; |
| } |
| |
| public Builder staticFramework() { |
| // TODO: load this from resources |
| return staticFramework("4.0.0-SNAPSHOT"); |
| } |
| |
| public Builder staticFramework(String version) { |
| String staticFeaturesKar = String.format(STATIC_FEATURES_KAR, version); |
| return this.defaultStage(Startup).useReferenceUrls().kars(Startup, true, staticFeaturesKar); |
| } |
| |
| public Builder blacklistProfiles(Collection<String> profiles) { |
| this.blacklistedProfiles.addAll(profiles); |
| return this; |
| } |
| |
| public Builder blacklistFeatures(Collection<String> features) { |
| this.blacklistedFeatures.addAll(features); |
| return this; |
| } |
| |
| public Builder blacklistBundles(Collection<String> bundles) { |
| this.blacklistedBundles.addAll(bundles); |
| return this; |
| } |
| |
| public Builder blacklistPolicy(BlacklistPolicy policy) { |
| this.blacklistPolicy = policy; |
| return this; |
| } |
| |
| /** |
| * Specify a set of edits to apply when moving etc files. |
| * @param propertyEdits the edits. |
| * @return this. |
| */ |
| public Builder propertyEdits(KarafPropertyEdits propertyEdits) { |
| this.propertyEdits = propertyEdits; |
| return this; |
| } |
| |
| public List<String> getBlacklistedProfiles() { |
| return blacklistedProfiles; |
| } |
| |
| public List<String> getBlacklistedFeatures() { |
| return blacklistedFeatures; |
| } |
| |
| public List<String> getBlacklistedBundles() { |
| return blacklistedBundles; |
| } |
| |
| public BlacklistPolicy getBlacklistPolicy() { |
| return blacklistPolicy; |
| } |
| |
| public void generateAssembly() throws Exception { |
| if (javase == null) { |
| throw new IllegalArgumentException("javase is not set"); |
| } |
| if (homeDirectory == null) { |
| throw new IllegalArgumentException("homeDirectory is not set"); |
| } |
| try { |
| doGenerateAssembly(); |
| } finally { |
| if (executor != null) { |
| executor.shutdownNow(); |
| } |
| } |
| } |
| |
| private void doGenerateAssembly() throws Exception { |
| systemDirectory = homeDirectory.resolve("system"); |
| etcDirectory = homeDirectory.resolve("etc"); |
| |
| LOGGER.info("Generating karaf assembly: " + homeDirectory); |
| |
| // |
| // Create download manager |
| // |
| Dictionary<String, String> props = new Hashtable<>(); |
| if (offline) { |
| props.put(ORG_OPS4J_PAX_URL_MVN_PID + "offline", "true"); |
| } |
| if (localRepository != null) { |
| props.put(Builder.ORG_OPS4J_PAX_URL_MVN_PID + ".localRepository", localRepository); |
| } |
| if (mavenRepositories != null) { |
| props.put(Builder.ORG_OPS4J_PAX_URL_MVN_PID + ".repositories", mavenRepositories); |
| } |
| MavenResolver resolver = MavenResolvers.createMavenResolver(props, ORG_OPS4J_PAX_URL_MVN_PID); |
| executor = Executors.newScheduledThreadPool(8); |
| manager = new CustomDownloadManager(resolver, executor); |
| this.resolver = new ResolverImpl(new Slf4jResolverLog(LOGGER)); |
| |
| // |
| // Unzip kars |
| // |
| LOGGER.info("Unzipping kars"); |
| Map<String, RepositoryInfo> repositories = new LinkedHashMap<>(this.repositories); |
| Downloader downloader = manager.createDownloader(); |
| for (String kar : kars.keySet()) { |
| downloader.download(kar, null); |
| } |
| downloader.await(); |
| for (String karUri : kars.keySet()) { |
| Kar kar = new Kar(manager.getProviders().get(karUri).getFile().toURI()); |
| kar.extract(systemDirectory.toFile(), homeDirectory.toFile()); |
| RepositoryInfo info = kars.get(karUri); |
| for (URI repositoryUri : kar.getFeatureRepos()) { |
| repositories.put(repositoryUri.toString(), info); |
| } |
| } |
| |
| // |
| // Propagate feature installation from repositories |
| // |
| Map<String, Stage> features = new LinkedHashMap<>(this.features); |
| Map<String, Features> karRepositories = loadRepositories(manager, repositories.keySet(), false); |
| for (String repo : repositories.keySet()) { |
| RepositoryInfo info = repositories.get(repo); |
| if (info.addAll) { |
| for (Feature feature : karRepositories.get(repo).getFeature()) { |
| features.put(feature.getId(), info.stage); |
| } |
| } |
| } |
| |
| // |
| // Load profiles |
| // |
| LOGGER.info("Loading profiles"); |
| allProfiles = new HashMap<>(); |
| for (String profilesUri : profilesUris) { |
| String uri = profilesUri; |
| if (uri.startsWith("jar:") && uri.contains("!/")) { |
| uri = uri.substring("jar:".length(), uri.indexOf("!/")); |
| } |
| if (!uri.startsWith("file:")) { |
| downloader = manager.createDownloader(); |
| downloader.download(uri, null); |
| downloader.await(); |
| StreamProvider provider = manager.getProviders().get(uri); |
| profilesUri = profilesUri.replace(uri, provider.getFile().toURI().toString()); |
| } |
| URI profileURI = URI.create(profilesUri); |
| Path profilePath; |
| try { |
| profilePath = Paths.get(profileURI); |
| } catch (FileSystemNotFoundException e) { |
| // file system does not exist, try to create it |
| FileSystem fs = FileSystems.newFileSystem(profileURI, new HashMap<String, Object>(), Builder.class.getClassLoader()); |
| profilePath = fs.provider().getPath(profileURI); |
| } |
| allProfiles.putAll(Profiles.loadProfiles(profilePath)); |
| // Handle blacklisted profiles |
| if (!blacklistedProfiles.isEmpty()) { |
| if (blacklistPolicy == BlacklistPolicy.Discard) { |
| // Override blacklisted profiles with empty ones |
| for (String profile : blacklistedProfiles) { |
| allProfiles.put(profile, ProfileBuilder.Factory.create(profile).getProfile()); |
| } |
| } else { |
| // Remove profiles completely |
| allProfiles.keySet().removeAll(blacklistedProfiles); |
| } |
| } |
| } |
| |
| // Generate profiles |
| Profile startupProfile = generateProfile(Stage.Startup, profiles, repositories, features, bundles); |
| Profile bootProfile = generateProfile(Stage.Boot, profiles, repositories, features, bundles); |
| Profile installedProfile = generateProfile(Stage.Installed, profiles, repositories, features, bundles); |
| |
| // |
| // Compute overall profile |
| // |
| Profile overallProfile = ProfileBuilder.Factory.create(UUID.randomUUID().toString()) |
| .setParents(Arrays.asList(startupProfile.getId(), bootProfile.getId(), installedProfile.getId())) |
| .getProfile(); |
| Profile overallOverlay = Profiles.getOverlay(overallProfile, allProfiles, environment); |
| Profile overallEffective = Profiles.getEffective(overallOverlay, false); |
| |
| manager = new CustomDownloadManager(resolver, executor, overallEffective); |
| |
| Hashtable<String, String> agentProps = new Hashtable<>(overallEffective.getConfiguration(ORG_OPS4J_PAX_URL_MVN_PID)); |
| final Map<String, String> properties = new HashMap<>(); |
| properties.put("karaf.default.repository", "system"); |
| InterpolationHelper.performSubstitution(agentProps, new InterpolationHelper.SubstitutionCallback() { |
| @Override |
| public String getValue(String key) { |
| return properties.get(key); |
| } |
| }, false, false, true); |
| |
| Map<String, List<KarafPropertyEdit>> editsByFile = new HashMap<>(); |
| |
| // |
| // Write config and system properties |
| // |
| Path configPropertiesPath = etcDirectory.resolve("config.properties"); |
| Properties configProperties = new Properties(configPropertiesPath.toFile()); |
| configProperties.putAll(overallEffective.getConfig()); |
| configProperties.save(); |
| |
| Path systemPropertiesPath = etcDirectory.resolve("system.properties"); |
| Properties systemProperties = new Properties(systemPropertiesPath.toFile()); |
| systemProperties.putAll(overallEffective.getSystem()); |
| systemProperties.save(); |
| |
| // |
| // Download libraries |
| // |
| // TODO: handle karaf 2.x and 3.x libraries |
| LOGGER.info("Downloading libraries"); |
| downloader = manager.createDownloader(); |
| downloadLibraries(downloader, configProperties, overallEffective.getLibraries()); |
| downloadLibraries(downloader, configProperties, libraries); |
| downloader.await(); |
| // Reformat clauses |
| reformatClauses(configProperties, Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); |
| reformatClauses(configProperties, Constants.FRAMEWORK_BOOTDELEGATION); |
| configProperties.save(); |
| |
| // |
| // Write all configuration files |
| // |
| for (Map.Entry<String, byte[]> config : overallEffective.getFileConfigurations().entrySet()) { |
| Path configFile = etcDirectory.resolve(config.getKey()); |
| Files.createDirectories(configFile.getParent()); |
| Files.write(configFile, config.getValue()); |
| } |
| |
| // |
| // Handle overrides |
| // |
| if (!overallEffective.getOverrides().isEmpty()) { |
| Path overrides = etcDirectory.resolve("overrides.properties"); |
| List<String> lines = new ArrayList<>(); |
| lines.add("#"); |
| lines.add("# Generated by the karaf assembly builder"); |
| lines.add("#"); |
| lines.addAll(overallEffective.getOverrides()); |
| Files.write(overrides, lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); |
| } |
| |
| // |
| // Handle blacklist |
| // |
| if (!blacklistedFeatures.isEmpty() || !blacklistedBundles.isEmpty()) { |
| Path blacklist = etcDirectory.resolve("blacklisted.properties"); |
| List<String> lines = new ArrayList<>(); |
| lines.add("#"); |
| lines.add("# Generated by the karaf assembly builder"); |
| lines.add("#"); |
| if (!blacklistedFeatures.isEmpty()) { |
| lines.add(""); |
| lines.add("# Features"); |
| lines.addAll(blacklistedFeatures); |
| } |
| if (!blacklistedBundles.isEmpty()) { |
| lines.add(""); |
| lines.add("# Bundles"); |
| lines.addAll(blacklistedBundles); |
| } |
| Files.write(blacklist, lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); |
| } |
| |
| // |
| // Startup stage |
| // |
| Profile startupEffective = startupStage(startupProfile); |
| |
| // |
| // Boot stage |
| // |
| Set<Feature> allBootFeatures = bootStage(bootProfile, startupEffective); |
| |
| // |
| // Installed stage |
| // |
| installStage(installedProfile, allBootFeatures); |
| |
| // 'improve' configuration files. |
| if (propertyEdits != null) { |
| KarafPropertiesEditor editor = new KarafPropertiesEditor(); |
| editor.setInputEtc(etcDirectory.toFile()) |
| .setOutputEtc(etcDirectory.toFile()) |
| .setEdits(propertyEdits); |
| editor.run(); |
| } |
| } |
| |
| private void reformatClauses(Properties config, String key) { |
| String val = config.getProperty(key); |
| if (val != null && !val.isEmpty()) { |
| List<String> comments = config.getComments(key); |
| Clause[] clauses = org.apache.felix.utils.manifest.Parser.parseHeader(val); |
| Set<String> strings = new LinkedHashSet<>(); |
| for (Clause clause : clauses) { |
| strings.add(clause.toString()); |
| } |
| List<String> lines = new ArrayList<>(); |
| lines.add(""); |
| int index = 0; |
| for (String string : strings) { |
| String s = " " + string; |
| if (index++ < strings.size() - 1) { |
| s += ", "; |
| } |
| lines.add(s); |
| } |
| config.put(key, comments, lines); |
| } |
| } |
| |
| protected void downloadLibraries(Downloader downloader, final Properties config, Collection<String> libraries) throws MalformedURLException { |
| Clause[] clauses = org.apache.felix.utils.manifest.Parser.parseClauses(libraries.toArray(new String[libraries.size()])); |
| for (final Clause clause : clauses) { |
| final String filename; |
| final String library; |
| if (clause.getDirective("url") != null) { |
| filename = clause.getName(); |
| library = clause.getDirective("url"); |
| } else { |
| filename = null; |
| library = clause.getName(); |
| } |
| final String type = clause.getDirective(LIBRARY_CLAUSE_TYPE) != null |
| ? clause.getDirective(LIBRARY_CLAUSE_TYPE) : Library.TYPE_DEFAULT; |
| final String path; |
| switch (type) { |
| case Library.TYPE_ENDORSED: path = "lib/endorsed"; break; |
| case Library.TYPE_EXTENSION: path = "lib/ext"; break; |
| case Library.TYPE_BOOT: path = "lib/boot"; break; |
| default: path = "lib"; break; |
| } |
| downloader.download(library, new DownloadCallback() { |
| @Override |
| public void downloaded(final StreamProvider provider) throws Exception { |
| synchronized (provider) { |
| Path input = provider.getFile().toPath(); |
| String name = filename != null ? filename : input.getFileName().toString(); |
| if (provider.getUrl().startsWith("mvn:")) { |
| Path libOutput = homeDirectory.resolve(path).resolve(name); |
| Files.copy(input, libOutput, StandardCopyOption.REPLACE_EXISTING); |
| // copy boot library in system repository |
| if (type.equals(Library.TYPE_BOOT)) { |
| String mvnPath = Parser.pathFromMaven(provider.getUrl()); |
| Path sysOutput = systemDirectory.resolve(mvnPath); |
| Files.createDirectories(sysOutput.getParent()); |
| Files.copy(input, sysOutput, StandardCopyOption.REPLACE_EXISTING); |
| libOutput = homeDirectory.resolve(path).resolve(name); |
| // copy the file |
| Files.copy(input, libOutput, StandardCopyOption.REPLACE_EXISTING); |
| /* a symlink could be used instead |
| |
| if (Files.notExists(libOutput, LinkOption.NOFOLLOW_LINKS)) { |
| try { |
| Files.createSymbolicLink(libOutput, libOutput.getParent().relativize(sysOutput)); |
| } catch (FileSystemException e) { |
| Files.copy(input, libOutput, StandardCopyOption.REPLACE_EXISTING); |
| } |
| } |
| */ |
| } |
| } else { |
| Path output = homeDirectory.resolve(path).resolve(name); |
| Files.copy(input, output, StandardCopyOption.REPLACE_EXISTING); |
| } |
| } |
| boolean export = Boolean.parseBoolean(clause.getDirective(LIBRARY_CLAUSE_EXPORT)); |
| boolean delegate = Boolean.parseBoolean(clause.getDirective(LIBRARY_CLAUSE_DELEGATE)); |
| if (export || delegate) { |
| Map<String, String> headers = getHeaders(provider); |
| String packages = headers.get(Constants.EXPORT_PACKAGE); |
| if (packages != null) { |
| Clause[] clauses = org.apache.felix.utils.manifest.Parser.parseHeader(packages); |
| if (export) { |
| String val = config.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); |
| for (Clause clause : clauses) { |
| val += "," + clause.toString(); |
| } |
| config.setProperty(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, val); |
| } |
| if (delegate) { |
| String val = config.getProperty(Constants.FRAMEWORK_BOOTDELEGATION); |
| for (Clause clause : clauses) { |
| val += "," + clause.getName(); |
| } |
| config.setProperty(Constants.FRAMEWORK_BOOTDELEGATION, val); |
| } |
| } |
| } |
| } |
| }); |
| } |
| } |
| |
| private void installStage(Profile installedProfile, Set<Feature> allBootFeatures) throws Exception { |
| // |
| // Handle installed profiles |
| // |
| Profile installedOverlay = Profiles.getOverlay(installedProfile, allProfiles, environment); |
| Profile installedEffective = Profiles.getEffective(installedOverlay, false); |
| |
| Downloader downloader = manager.createDownloader(); |
| |
| // Load startup repositories |
| Map<String, Features> installedRepositories = loadRepositories(manager, installedEffective.getRepositories(), true); |
| // Compute startup feature dependencies |
| Set<Feature> allInstalledFeatures = new LinkedHashSet<>(); |
| for (Features repo : installedRepositories.values()) { |
| allInstalledFeatures.addAll(repo.getFeature()); |
| } |
| Set<Feature> installedFeatures = new LinkedHashSet<>(); |
| // Add boot features for search |
| allInstalledFeatures.addAll(allBootFeatures); |
| for (String feature : installedEffective.getFeatures()) { |
| addFeatures(allInstalledFeatures, feature, installedFeatures, true); |
| } |
| for (Feature feature : installedFeatures) { |
| for (Bundle bundle : feature.getBundle()) { |
| if (!ignoreDependencyFlag || !bundle.isDependency()) { |
| installArtifact(downloader, bundle.getLocation().trim()); |
| } |
| } |
| // Install config files |
| for (ConfigFile configFile : feature.getConfigfile()) { |
| installArtifact(downloader, configFile.getLocation().trim()); |
| } |
| for (Conditional cond : feature.getConditional()) { |
| for (Bundle bundle : cond.getBundle()) { |
| if (!ignoreDependencyFlag || !bundle.isDependency()) { |
| installArtifact(downloader, bundle.getLocation().trim()); |
| } |
| } |
| } |
| } |
| for (String location : installedEffective.getBundles()) { |
| installArtifact(downloader, location); |
| } |
| downloader.await(); |
| } |
| |
| private Set<Feature> bootStage(Profile bootProfile, Profile startupEffective) throws Exception { |
| // |
| // Handle boot profiles |
| // |
| Profile bootOverlay = Profiles.getOverlay(bootProfile, allProfiles, environment); |
| Profile bootEffective = Profiles.getEffective(bootOverlay, false); |
| // Load startup repositories |
| Map<String, Features> bootRepositories = loadRepositories(manager, bootEffective.getRepositories(), true); |
| // Compute startup feature dependencies |
| Set<Feature> allBootFeatures = new LinkedHashSet<>(); |
| for (Features repo : bootRepositories.values()) { |
| allBootFeatures.addAll(repo.getFeature()); |
| } |
| // Generate a global feature |
| Map<String, Dependency> generatedDep = new LinkedHashMap<>(); |
| Feature generated = new Feature(); |
| generated.setName(UUID.randomUUID().toString()); |
| // Add feature dependencies |
| for (String dependency : bootEffective.getFeatures()) { |
| Dependency dep = generatedDep.get(dependency); |
| if (dep == null) { |
| dep = createDependency(dependency); |
| generated.getFeature().add(dep); |
| generatedDep.put(dep.getName(), dep); |
| } |
| dep.setDependency(false); |
| } |
| // Add bundles |
| for (String location : bootEffective.getBundles()) { |
| location = location.replace("profile:", "file:etc/"); |
| Bundle bun = new Bundle(); |
| bun.setLocation(location); |
| generated.getBundle().add(bun); |
| } |
| Features rep = new Features(); |
| rep.setName(UUID.randomUUID().toString()); |
| rep.getRepository().addAll(bootEffective.getRepositories()); |
| rep.getFeature().add(generated); |
| allBootFeatures.add(generated); |
| |
| Downloader downloader = manager.createDownloader(); |
| |
| // Compute startup feature dependencies |
| Set<Feature> bootFeatures = new LinkedHashSet<>(); |
| addFeatures(allBootFeatures, generated.getName(), bootFeatures, true); |
| for (Feature feature : bootFeatures) { |
| // the feature is a startup feature, updating startup.properties file |
| LOGGER.info("Feature " + feature.getName() + " is defined as a boot feature"); |
| // add the feature in the system folder |
| Set<String> locations = new LinkedHashSet<>(); |
| for (Bundle bundle : feature.getBundle()) { |
| if (!ignoreDependencyFlag || !bundle.isDependency()) { |
| locations.add(bundle.getLocation().trim()); |
| } |
| } |
| for (Conditional cond : feature.getConditional()) { |
| for (Bundle bundle : cond.getBundle()) { |
| if (!ignoreDependencyFlag || !bundle.isDependency()) { |
| locations.add(bundle.getLocation().trim()); |
| } |
| } |
| } |
| |
| // Build optional features and known prerequisites |
| Map<String, List<String>> prereqs = new HashMap<>(); |
| prereqs.put("blueprint:", Arrays.asList("deployer", "aries-blueprint")); |
| prereqs.put("spring:", Arrays.asList("deployer", "spring")); |
| prereqs.put("wrap:", Arrays.asList("wrap")); |
| prereqs.put("war:", Arrays.asList("war")); |
| for (String location : locations) { |
| installArtifact(downloader, location); |
| for (Map.Entry<String, List<String>> entry : prereqs.entrySet()) { |
| if (location.startsWith(entry.getKey())) { |
| for (String prereq : entry.getValue()) { |
| Dependency dep = generatedDep.get(prereq); |
| if (dep == null) { |
| dep = new Dependency(); |
| dep.setName(prereq); |
| generated.getFeature().add(dep); |
| generatedDep.put(dep.getName(), dep); |
| } |
| dep.setPrerequisite(true); |
| } |
| } |
| } |
| } |
| // Install config files |
| for (ConfigFile configFile : feature.getConfigfile()) { |
| installArtifact(downloader, configFile.getLocation().trim()); |
| } |
| for (Conditional cond : feature.getConditional()) { |
| for (ConfigFile configFile : cond.getConfigfile()) { |
| installArtifact(downloader, configFile.getLocation().trim()); |
| } |
| } |
| // Install libraries |
| List<String> libraries = new ArrayList<>(); |
| for (Library library : feature.getLibraries()) { |
| String lib = library.getLocation() + |
| ";type:=" + library.getType() + |
| ";export:=" + library.isExport() + |
| ";delegate:=" + library.isDelegate(); |
| libraries.add(lib); |
| } |
| Path configPropertiesPath = etcDirectory.resolve("config.properties"); |
| Properties configProperties = new Properties(configPropertiesPath.toFile()); |
| downloadLibraries(downloader, configProperties, libraries); |
| downloader.await(); |
| // Reformat clauses |
| reformatClauses(configProperties, Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); |
| reformatClauses(configProperties, Constants.FRAMEWORK_BOOTDELEGATION); |
| configProperties.save(); |
| } |
| |
| // If there are bundles to install, we can't use the boot features only |
| // so keep the generated feature |
| Path featuresCfgFile = etcDirectory.resolve("org.apache.karaf.features.cfg"); |
| if (!generated.getBundle().isEmpty()) { |
| File output = etcDirectory.resolve(rep.getName() + ".xml").toFile(); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| JaxbUtil.marshal(rep, baos); |
| ByteArrayInputStream bais; |
| String repoUrl; |
| if (karafVersion == KarafVersion.v24) { |
| String str = baos.toString(); |
| str = str.replace("http://karaf.apache.org/xmlns/features/v1.3.0", "http://karaf.apache.org/xmlns/features/v1.2.0"); |
| str = str.replaceAll(" dependency=\".*?\"", ""); |
| str = str.replaceAll(" prerequisite=\".*?\"", ""); |
| for (Feature f : rep.getFeature()) { |
| for (Dependency d : f.getFeature()) { |
| if (d.isPrerequisite()) { |
| if (!startupEffective.getFeatures().contains(d.getName())) { |
| LOGGER.warn("Feature " + d.getName() + " is a prerequisite and should be installed as a startup feature."); } |
| } |
| } |
| } |
| bais = new ByteArrayInputStream(str.getBytes()); |
| repoUrl = "file:etc/" + output.getName(); |
| } else { |
| bais = new ByteArrayInputStream(baos.toByteArray()); |
| repoUrl = "file:${karaf.home}/etc/" + output.getName(); |
| } |
| Files.copy(bais, output.toPath()); |
| Properties featuresProperties = new Properties(featuresCfgFile.toFile()); |
| featuresProperties.put(FEATURES_REPOSITORIES, repoUrl); |
| featuresProperties.put(FEATURES_BOOT, generated.getName()); |
| featuresProperties.save(); |
| } |
| else { |
| String repos = getRepos(rep); |
| String boot = getBootFeatures(generatedDep); |
| |
| Properties featuresProperties = new Properties(featuresCfgFile.toFile()); |
| featuresProperties.put(FEATURES_REPOSITORIES, repos); |
| featuresProperties.put(FEATURES_BOOT, boot); |
| reformatClauses(featuresProperties, FEATURES_REPOSITORIES); |
| reformatClauses(featuresProperties, FEATURES_BOOT); |
| featuresProperties.save(); |
| } |
| downloader.await(); |
| return allBootFeatures; |
| } |
| |
| private String getRepos(Features rep) { |
| StringBuilder repos = new StringBuilder(); |
| for (String repo : new LinkedHashSet<>(rep.getRepository())) { |
| if (repos.length() > 0) { |
| repos.append(","); |
| } |
| repos.append(repo); |
| } |
| return repos.toString(); |
| } |
| |
| private String getBootFeatures(Map<String, Dependency> generatedDep) { |
| StringBuilder boot = new StringBuilder(); |
| for (Dependency dep : generatedDep.values()) { |
| if (dep.isPrerequisite()) { |
| if (boot.length() == 0) { |
| boot.append("("); |
| } else { |
| boot.append(","); |
| } |
| boot.append(dep.getName()); |
| } |
| } |
| if (boot.length() > 0) { |
| boot.append(")"); |
| } |
| // TODO: for dependencies, we'd need to resolve the features completely |
| for (Dependency dep : generatedDep.values()) { |
| if (!dep.isPrerequisite() && !dep.isDependency()) { |
| if (boot.length() > 0) { |
| boot.append(","); |
| } |
| boot.append(dep.getName()); |
| if (!Feature.DEFAULT_VERSION.equals(dep.getVersion())) { |
| if (karafVersion == KarafVersion.v4x) { |
| boot.append("/"); |
| } else { |
| boot.append(";version="); |
| } |
| boot.append(dep.getVersion()); |
| } |
| } |
| } |
| return boot.toString(); |
| } |
| |
| private Dependency createDependency(String dependency) { |
| Dependency dep; |
| dep = new Dependency(); |
| String[] split = dependency.split("/"); |
| dep.setName(split[0]); |
| if (split.length > 1) { |
| dep.setVersion(split[1]); |
| } |
| return dep; |
| } |
| |
| private Profile startupStage(Profile startupProfile) throws Exception { |
| // |
| // Compute startup |
| // |
| Profile startupOverlay = Profiles.getOverlay(startupProfile, allProfiles, environment); |
| Profile startupEffective = Profiles.getEffective(startupOverlay, false); |
| // Load startup repositories |
| LOGGER.info("Loading repositories"); |
| Map<String, Features> startupRepositories = loadRepositories(manager, startupEffective.getRepositories(), false); |
| |
| // |
| // Resolve |
| // |
| LOGGER.info("Resolving features"); |
| Map<String, Integer> bundles = |
| resolve(manager, |
| resolver, |
| startupRepositories.values(), |
| startupEffective.getFeatures(), |
| startupEffective.getBundles(), |
| startupEffective.getOverrides(), |
| startupEffective.getOptionals()); |
| |
| // |
| // Generate startup.properties |
| // |
| Properties startup = new Properties(); |
| startup.setHeader(Collections.singletonList("# Bundles to be started on startup, with startlevel")); |
| Map<Integer, Set<String>> invertedStartupBundles = MapUtils.invert(bundles); |
| for (Map.Entry<Integer, Set<String>> entry : invertedStartupBundles.entrySet()) { |
| String startLevel = Integer.toString(entry.getKey()); |
| for (String location : new TreeSet<>(entry.getValue())) { |
| // remove any custom deployer prefix |
| int mvnIndex = location.indexOf("mvn:"); |
| if (mvnIndex > 0) { |
| location = location.substring(mvnIndex); |
| } |
| if (useReferenceUrls) { |
| if (location.startsWith("mvn:")) { |
| location = "file:" + Parser.pathFromMaven(location); |
| } |
| if (location.startsWith("file:")) { |
| location = "reference:" + location; |
| } |
| } |
| if (location.startsWith("file:") && karafVersion == KarafVersion.v24) { |
| location = location.substring("file:".length()); |
| } |
| startup.put(location, startLevel); |
| } |
| } |
| Path startupProperties = etcDirectory.resolve("startup.properties"); |
| startup.save(startupProperties.toFile()); |
| return startupEffective; |
| } |
| |
| private void installArtifact(Downloader downloader, String location) throws Exception { |
| LOGGER.info("== Installing artifact " + location); |
| location = DownloadManagerHelper.stripUrl(location); |
| // remove any custom deployer prefix |
| int mvnIndex = location.indexOf("mvn:"); |
| if (mvnIndex > 0) { |
| location = location.substring(mvnIndex); |
| } |
| if (location.startsWith("mvn:")) { |
| if (location.endsWith("/")) { |
| // for bad formed URL (like in Camel for mustache-compiler), we remove the trailing / |
| location = location.substring(0, location.length() - 1); |
| } |
| downloader.download(location, new DownloadCallback() { |
| @Override |
| public void downloaded(final StreamProvider provider) throws Exception { |
| String uri = provider.getUrl(); |
| if (Blacklist.isBundleBlacklisted(blacklistedBundles, uri)) { |
| throw new RuntimeException("Bundle " + uri + " is blacklisted"); |
| } |
| Path path = pathFromProviderUrl(uri); |
| synchronized (provider) { |
| Files.createDirectories(path.getParent()); |
| Files.copy(provider.getFile().toPath(), path, StandardCopyOption.REPLACE_EXISTING); |
| } |
| } |
| }); |
| } else { |
| LOGGER.warn("Ignoring artifact " + location); |
| } |
| } |
| |
| private void addFeatures(Set<Feature> allFeatures, String feature, Set<Feature> features, boolean mandatory) { |
| String name; |
| VersionRange range; |
| int idx = feature.indexOf('/'); |
| if (idx > 0) { |
| name = feature.substring(0, idx); |
| String version = feature.substring(idx + 1); |
| version = version.trim(); |
| if (version.equals(org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION)) { |
| range = new VersionRange(Version.emptyVersion); |
| } else { |
| range = new VersionRange(version, true, true); |
| } |
| } else { |
| name = feature; |
| range = new VersionRange(Version.emptyVersion); |
| } |
| Set<Feature> set = new LinkedHashSet<>(); |
| for (Feature f : allFeatures) { |
| if (f.getName().equals(name) && range.contains(VersionTable.getVersion(f.getVersion()))) { |
| set.add(f); |
| } |
| } |
| if (mandatory && set.isEmpty()) { |
| throw new IllegalStateException("Could not find matching feature for " + feature); |
| } |
| for (Feature f : set) { |
| features.add(f); |
| for (Dependency dep : f.getFeature()) { |
| addFeatures(allFeatures, dep.toString(), features, !dep.isDependency() && !dep.isPrerequisite()); |
| } |
| } |
| } |
| |
| private String getFeatureSt(Dependency dep) { |
| String version = dep.getVersion() == null || "0.0.0".equals(dep.getVersion()) ? "" : "/" + dep.getVersion(); |
| return dep.getName() + version; |
| } |
| |
| /** |
| * Checks if a given feature f matches the featureRef. |
| * TODO Need to also check for version ranges. Currently ranges are ignored and all features matching the name |
| * are copied in that case. |
| * |
| * @param f |
| * @param featureRef |
| * @return |
| */ |
| private boolean matches(Feature f, Dependency featureRef) { |
| if (!f.getName().equals(featureRef.getName())) { |
| return false; |
| } |
| |
| final String featureRefVersion = featureRef.getVersion(); |
| |
| if (featureRefVersion == null) { |
| return true; |
| } |
| |
| if (featureRefVersion.equals("0.0.0")) { |
| return true; |
| } |
| |
| if (featureRefVersion.startsWith("[")) { |
| return true; |
| } |
| |
| return VersionUtils.versionEquals(f.getVersion(), featureRefVersion); |
| } |
| |
| private List<String> getStaged(Stage stage, Map<String, Stage> data) { |
| List<String> staged = new ArrayList<>(); |
| for (String s : data.keySet()) { |
| if (data.get(s) == stage) { |
| staged.add(s); |
| } |
| } |
| return staged; |
| } |
| |
| private List<String> getStagedRepositories(Stage stage, Map<String, RepositoryInfo> data) { |
| List<String> staged = new ArrayList<>(); |
| for (String s : data.keySet()) { |
| if (data.get(s).stage == stage || |
| data.get(s).stage == Stage.Startup && stage == Stage.Boot) { |
| // For boot stage, we also want the startup repositories |
| staged.add(s); |
| } |
| } |
| return staged; |
| } |
| |
| private Map<String, Features> loadRepositories(DownloadManager manager, Collection<String> repositories, final boolean install) throws Exception { |
| final Map<String, Features> loaded = new HashMap<>(); |
| final Downloader downloader = manager.createDownloader(); |
| final List<String> blacklist = new ArrayList<>(); |
| blacklist.addAll(blacklistedBundles); |
| blacklist.addAll(blacklistedFeatures); |
| final Clause[] clauses = org.apache.felix.utils.manifest.Parser.parseClauses(blacklist.toArray(new String[blacklist.size()])); |
| for (String repository : repositories) { |
| downloader.download(repository, new DownloadCallback() { |
| @Override |
| public void downloaded(final StreamProvider provider) throws Exception { |
| String url = provider.getUrl(); |
| synchronized (loaded) { |
| if (!loaded.containsKey(provider.getUrl())) { |
| if (install) { |
| synchronized (provider) { |
| Path path = pathFromProviderUrl(url); |
| Files.createDirectories(path.getParent()); |
| Files.copy(provider.getFile().toPath(), path, StandardCopyOption.REPLACE_EXISTING); |
| } |
| } |
| try (InputStream is = provider.open()) { |
| Features featuresModel = JaxbUtil.unmarshal(url, is, false); |
| if (blacklistPolicy == BlacklistPolicy.Discard) { |
| Blacklist.blacklist(featuresModel, clauses); |
| } |
| loaded.put(provider.getUrl(), featuresModel); |
| for (String innerRepository : featuresModel.getRepository()) { |
| downloader.download(innerRepository, this); |
| } |
| } |
| } |
| } |
| } |
| }); |
| } |
| downloader.await(); |
| return loaded; |
| } |
| |
| private Profile generateProfile(Stage stage, Map<String, Stage> profiles, Map<String, RepositoryInfo> repositories, Map<String, Stage> features, Map<String, Stage> bundles) { |
| Profile profile = ProfileBuilder.Factory.create(UUID.randomUUID().toString()) |
| .setParents(getStaged(stage, profiles)) |
| .setRepositories(getStagedRepositories(stage, repositories)) |
| .setFeatures(getStaged(stage, features)) |
| .setBundles(getStaged(stage, bundles)) |
| .getProfile(); |
| allProfiles.put(profile.getId(), profile); |
| return profile; |
| } |
| |
| private Map<String, Integer> resolve( |
| DownloadManager manager, |
| Resolver resolver, |
| Collection<Features> repositories, |
| Collection<String> features, |
| Collection<String> bundles, |
| Collection<String> overrides, |
| Collection<String> optionals) throws Exception { |
| BundleRevision systemBundle = getSystemBundle(); |
| AssemblyDeployCallback callback = new AssemblyDeployCallback(manager, this, systemBundle, repositories); |
| Deployer deployer = new Deployer(manager, resolver, callback); |
| |
| // Install framework |
| Deployer.DeploymentRequest request = createDeploymentRequest(); |
| // Add overrides |
| request.overrides.addAll(overrides); |
| // Add optional resources |
| final List<Resource> resources = new ArrayList<>(); |
| Downloader downloader = manager.createDownloader(); |
| for (String optional : optionals) { |
| downloader.download(optional, new DownloadCallback() { |
| @Override |
| public void downloaded(StreamProvider provider) throws Exception { |
| Resource resource = ResourceBuilder.build(provider.getUrl(), getHeaders(provider)); |
| synchronized (resources) { |
| resources.add(resource); |
| } |
| } |
| }); |
| } |
| downloader.await(); |
| request.globalRepository = new BaseRepository(resources); |
| // Install features |
| for (String feature : features) { |
| MapUtils.addToMapSet(request.requirements, FeaturesService.ROOT_REGION, feature); |
| } |
| for (String bundle : bundles) { |
| MapUtils.addToMapSet(request.requirements, FeaturesService.ROOT_REGION, "bundle:" + bundle); |
| } |
| Set<String> prereqs = new LinkedHashSet<>(); |
| while (true) { |
| try { |
| deployer.deploy(callback.getDeploymentState(), request); |
| break; |
| } catch (Deployer.PartialDeploymentException e) { |
| if (!prereqs.containsAll(e.getMissing())) { |
| prereqs.addAll(e.getMissing()); |
| } else { |
| throw new Exception("Deployment aborted due to loop in missing prerequisites: " + e.getMissing()); |
| } |
| } |
| } |
| |
| return callback.getStartupBundles(); |
| } |
| |
| private Deployer.DeploymentRequest createDeploymentRequest() { |
| Deployer.DeploymentRequest request = new Deployer.DeploymentRequest(); |
| request.bundleUpdateRange = FeaturesService.DEFAULT_BUNDLE_UPDATE_RANGE; |
| request.featureResolutionRange = FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE; |
| request.serviceRequirements = FeaturesService.SERVICE_REQUIREMENTS_DEFAULT; |
| request.overrides = new LinkedHashSet<>(); |
| request.requirements = new HashMap<>(); |
| request.stateChanges = new HashMap<>(); |
| request.options = EnumSet.noneOf(FeaturesService.Option.class); |
| return request; |
| } |
| |
| private BundleRevision getSystemBundle() throws Exception { |
| Path configPropPath = etcDirectory.resolve("config.properties"); |
| Properties configProps = PropertiesLoader.loadPropertiesOrFail(configPropPath.toFile()); |
| configProps.put("java.specification.version", javase); |
| configProps.substitute(); |
| |
| Attributes attributes = new Attributes(); |
| attributes.putValue(Constants.BUNDLE_MANIFESTVERSION, "2"); |
| attributes.putValue(Constants.BUNDLE_SYMBOLICNAME, "system.bundle"); |
| attributes.putValue(Constants.BUNDLE_VERSION, "0.0.0"); |
| |
| String exportPackages = configProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); |
| if (configProps.containsKey(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA)) { |
| exportPackages += "," + configProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); |
| } |
| exportPackages = exportPackages.replaceAll(",\\s*,", ","); |
| attributes.putValue(Constants.EXPORT_PACKAGE, exportPackages); |
| |
| String systemCaps = configProps.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES); |
| attributes.putValue(Constants.PROVIDE_CAPABILITY, systemCaps); |
| |
| final Hashtable<String, String> headers = new Hashtable<>(); |
| for (Map.Entry attr : attributes.entrySet()) { |
| headers.put(attr.getKey().toString(), attr.getValue().toString()); |
| } |
| |
| return new FakeBundleRevision(headers, "system-bundle", 0l); |
| } |
| |
| Map<String, String> getHeaders(StreamProvider provider) throws IOException { |
| try ( |
| ZipInputStream zis = new ZipInputStream(provider.open()) |
| ) { |
| ZipEntry entry; |
| while ((entry = zis.getNextEntry()) != null) { |
| if (MANIFEST_NAME.equals(entry.getName())) { |
| Attributes attributes = new Manifest(zis).getMainAttributes(); |
| Map<String, String> headers = new HashMap<>(); |
| for (Map.Entry attr : attributes.entrySet()) { |
| headers.put(attr.getKey().toString(), attr.getValue().toString()); |
| } |
| return headers; |
| } |
| } |
| } |
| throw new IllegalArgumentException("Resource " + provider.getUrl() + " does not contain a manifest"); |
| } |
| |
| private Path pathFromProviderUrl(String url) throws MalformedURLException { |
| String pathString; |
| if (url.startsWith("file:")) { |
| return Paths.get(URI.create(url)); |
| } |
| else if (url.startsWith("mvn:")) { |
| pathString = Parser.pathFromMaven(url); |
| } |
| else { |
| pathString = url; |
| } |
| return systemDirectory.resolve(pathString); |
| } |
| |
| } |