http://git-wip-us.apache.org/repos/asf/karaf/blob/203cfd8d/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java index f0d9ab9..943992a 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java @@ -23,6 +23,8 @@ import java.util.List; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -432,6 +434,15 @@ public class Feature extends Content implements org.apache.karaf.features.Featur c.setOwner(this); } } + if (config != null) { + for (Config c : config) { + String v = c.getValue(); + v = Stream.of(v.split("\n")) + .map(String::trim) + .collect(Collectors.joining("\n", "", "\n")); + c.setValue(v); + } + } } @Override
http://git-wip-us.apache.org/repos/asf/karaf/blob/203cfd8d/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java index 7487e62..29b5eb7 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java @@ -26,8 +26,6 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.apache.felix.utils.properties.InterpolationHelper; import org.apache.felix.utils.properties.InterpolationHelper.SubstitutionCallback; @@ -102,7 +100,8 @@ public class FeatureConfigInstaller { public void installFeatureConfigs(Feature feature) throws IOException, InvalidSyntaxException { for (ConfigInfo config : feature.getConfigurations()) { - Properties props = config.getProperties(); + org.apache.felix.utils.properties.Properties props = new org.apache.felix.utils.properties.Properties(); + props.load(new StringReader(config.getValue())); String[] pid = parsePid(config.getName()); Configuration cfg = findExistingConfiguration(configAdmin, pid[0], pid[1]); if (cfg == null) { @@ -112,14 +111,14 @@ public class FeatureConfigInstaller { cfgProps.put(CONFIG_KEY, key); cfg.update(cfgProps); try { - updateStorage(pid[0], pid[1], props); + updateStorage(pid[0], pid[1], props, false); } catch (Exception e) { LOGGER.warn("Can't update cfg file", e); } } else if (config.isAppend()) { boolean update = false; Dictionary<String,Object> properties = cfg.getProperties(); - for (String key : props.stringPropertyNames()) { + for (String key : props.keySet()) { if (properties.get(key) == null) { properties.put(key, props.getProperty(key)); update = true; @@ -128,7 +127,7 @@ public class FeatureConfigInstaller { if (update) { cfg.update(properties); try { - updateStorage(pid[0], pid[1], properties); + updateStorage(pid[0], pid[1], props, true); } catch (Exception e) { LOGGER.warn("Can't update cfg file", e); } @@ -140,14 +139,11 @@ public class FeatureConfigInstaller { } } - private Dictionary<String, String> convertToDict(Properties props) { + private Dictionary<String, String> convertToDict(Map<String, String> props) { Dictionary<String, String> cfgProps = new Hashtable<String, String>(); - for (@SuppressWarnings("rawtypes") - Enumeration e = props.propertyNames(); e.hasMoreElements();) { - String key = (String) e.nextElement(); - String val = props.getProperty(key); - cfgProps.put(key, val); - } + for (Map.Entry<String, String> e : props.entrySet()) { + cfgProps.put(e.getKey(), e.getValue()); + } return cfgProps; } @@ -244,7 +240,7 @@ public class FeatureConfigInstaller { } } - protected void updateStorage(String pid, String factoryPid, Dictionary props) throws Exception { + protected void updateStorage(String pid, String factoryPid, org.apache.felix.utils.properties.Properties props, boolean append) throws Exception { if (storage != null && configCfgStore) { // get the cfg file File cfgFile; @@ -268,39 +264,50 @@ public class FeatureConfigInstaller { cfgFile = new File(new URL((String) val).toURI()); } } catch (Exception e) { - throw (IOException) new IOException(e.getMessage()).initCause(e); + throw new IOException(e.getMessage(), e); } } LOGGER.trace("Update {}", cfgFile.getName()); // update the cfg file - org.apache.felix.utils.properties.Properties properties = new org.apache.felix.utils.properties.Properties(cfgFile); - for (Enumeration<String> keys = props.keys(); keys.hasMoreElements(); ) { - String key = keys.nextElement(); - if (!Constants.SERVICE_PID.equals(key) - && !ConfigurationAdmin.SERVICE_FACTORYPID.equals(key) - && !FILEINSTALL_FILE_NAME.equals(key)) { - if (props.get(key) != null) { - properties.put(key, props.get(key).toString()); + if (!cfgFile.exists()) { + props.save(cfgFile); + } else { + org.apache.felix.utils.properties.Properties properties = new org.apache.felix.utils.properties.Properties(cfgFile); + for (String key : props.keySet()) { + if (!Constants.SERVICE_PID.equals(key) + && !ConfigurationAdmin.SERVICE_FACTORYPID.equals(key) + && !FILEINSTALL_FILE_NAME.equals(key)) { + List<String> comments = props.getComments(key); + List<String> value = props.getRaw(key); + if (!properties.containsKey(key)) { + properties.put(key, comments, value); + } else if (!append) { + if (comments.isEmpty()) { + comments = properties.getComments(key); + } + properties.put(key, comments, value); + } } } - } - // remove "removed" properties from the cfg file - ArrayList<String> propertiesToRemove = new ArrayList<>(); - for (String key : properties.keySet()) { - if (props.get(key) == null - && !Constants.SERVICE_PID.equals(key) - && !ConfigurationAdmin.SERVICE_FACTORYPID.equals(key) - && !FILEINSTALL_FILE_NAME.equals(key)) { - propertiesToRemove.add(key); + if (!append) { + // remove "removed" properties from the cfg file + ArrayList<String> propertiesToRemove = new ArrayList<>(); + for (String key : properties.keySet()) { + if (!props.containsKey(key) + && !Constants.SERVICE_PID.equals(key) + && !ConfigurationAdmin.SERVICE_FACTORYPID.equals(key) + && !FILEINSTALL_FILE_NAME.equals(key)) { + propertiesToRemove.add(key); + } + } + for (String key : propertiesToRemove) { + properties.remove(key); + } } + // save the cfg file + storage.mkdirs(); + properties.save(); } - for (String key : propertiesToRemove) { - properties.remove(key); - } - - // save the cfg file - storage.mkdirs(); - properties.save(); } } http://git-wip-us.apache.org/repos/asf/karaf/blob/203cfd8d/features/core/src/test/java/org/apache/karaf/features/internal/model/ConfigTest.java ---------------------------------------------------------------------- diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/model/ConfigTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/model/ConfigTest.java new file mode 100644 index 0000000..44cad3b --- /dev/null +++ b/features/core/src/test/java/org/apache/karaf/features/internal/model/ConfigTest.java @@ -0,0 +1,47 @@ +/* + * 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.features.internal.model; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ConfigTest { + + @Test + public void testTrim() { + Config config = new Config(); + config.setName("my.config"); + config.setValue(" # my comment\n" + + " my.key = my.value\n"); + + assertEquals("my.value", config.getProperties().getProperty("my.key")); + } + + @Test + public void testInterpolation() { + Config config = new Config(); + config.setName("my.config"); + config.setValue(" # my comment\n" + + " my.nb = 2\n" + + " my.key.1 = my.value.1\n" + + " my.key.2 = my.value.2\n" + + " my.key.3 = ab${my.key.${my.nb}}"); + + assertEquals("abmy.value.2", config.getProperties().getProperty("my.key.3")); + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/203cfd8d/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java ---------------------------------------------------------------------- diff --git a/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java b/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java index bba1427..0227c21 100644 --- a/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java +++ b/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java @@ -46,6 +46,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.jar.Attributes; import java.util.jar.Manifest; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -65,6 +66,7 @@ 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.Config; import org.apache.karaf.features.internal.model.ConfigFile; import org.apache.karaf.features.internal.model.Dependency; import org.apache.karaf.features.internal.model.Feature; @@ -158,6 +160,7 @@ public class Builder { String mavenRepositories; Map<String, String> config = new LinkedHashMap<>(); Map<String, String> system = new LinkedHashMap<>(); + List<String> pidsToExtract; private ScheduledExecutorService executor; private DownloadManager manager; @@ -369,6 +372,11 @@ public class Builder { return this; } + public Builder pidsToExtract(List<String> pidsToExtract) { + this.pidsToExtract = pidsToExtract; + return this; + } + /** * Specify a set of url mappings to use instead of * downloading from the original urls. @@ -406,6 +414,10 @@ public class Builder { return blacklistPolicy; } + public List<String> getPidsToExtract() { + return pidsToExtract; + } + public void generateAssembly() throws Exception { if (javase == null) { throw new IllegalArgumentException("javase is not set"); @@ -885,6 +897,19 @@ public class Builder { installArtifact(downloader, configFile.getLocation().trim()); } } + // Extract configs + for (Config config : feature.getConfig()) { + if (pidMatching(config.getName())) { + Files.write(etcDirectory.resolve(config.getName() + ".cfg"), config.getValue().getBytes()); + } + } + for (Conditional cond : feature.getConditional()) { + for (Config config : cond.getConfig()) { + if (pidMatching(config.getName())) { + Files.write(etcDirectory.resolve(config.getName() + ".cfg"), config.getValue().getBytes()); + } + } + } // Install libraries List<String> libraries = new ArrayList<>(); for (Library library : feature.getLibraries()) { @@ -953,6 +978,113 @@ public class Builder { return allBootFeatures; } + private boolean pidMatching(String name) { + if (pidsToExtract == null) { + return true; + } + for (String p : pidsToExtract) { + boolean negated = false; + if (p.startsWith("!")) { + negated = true; + p = p.substring(1); + } + String r = globToRegex(p); + if (Pattern.matches(r, name)) { + return !negated; + } + } + return false; + } + + private String globToRegex(String pattern) { + StringBuilder sb = new StringBuilder(pattern.length()); + int inGroup = 0; + int inClass = 0; + int firstIndexInClass = -1; + char[] arr = pattern.toCharArray(); + for (int i = 0; i < arr.length; i++) { + char ch = arr[i]; + switch (ch) { + case '\\': + if (++i >= arr.length) { + sb.append('\\'); + } else { + char next = arr[i]; + switch (next) { + case ',': + // escape not needed + break; + case 'Q': + case 'E': + // extra escape needed + sb.append('\\'); + default: + sb.append('\\'); + } + sb.append(next); + } + break; + case '*': + if (inClass == 0) + sb.append(".*"); + else + sb.append('*'); + break; + case '?': + if (inClass == 0) + sb.append('.'); + else + sb.append('?'); + break; + case '[': + inClass++; + firstIndexInClass = i + 1; + sb.append('['); + break; + case ']': + inClass--; + sb.append(']'); + break; + case '.': + case '(': + case ')': + case '+': + case '|': + case '^': + case '$': + case '@': + case '%': + if (inClass == 0 || (firstIndexInClass == i && ch == '^')) + sb.append('\\'); + sb.append(ch); + break; + case '!': + if (firstIndexInClass == i) + sb.append('^'); + else + sb.append('!'); + break; + case '{': + inGroup++; + sb.append('('); + break; + case '}': + inGroup--; + sb.append(')'); + break; + case ',': + if (inGroup > 0) + sb.append('|'); + else + sb.append(','); + break; + default: + sb.append(ch); + } + } + return sb.toString(); + } + private String getRepos(Features rep) { StringBuilder repos = new StringBuilder(); for (String repo : new HashSet<>(rep.getRepository())) { http://git-wip-us.apache.org/repos/asf/karaf/blob/203cfd8d/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java ---------------------------------------------------------------------- diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java index ee2c587..0bb61c8 100644 --- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java +++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java @@ -25,6 +25,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.attribute.PosixFilePermissions; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -232,6 +233,13 @@ public class AssemblyMojo extends MojoSupport { protected String propertyFileEdits; /** + * Glob specifying which configuration pids in the selected boot features + * should be extracted to the etc directory. + */ + @Parameter + protected List<String> pidsToExtract = Collections.singletonList("*"); + + /** * Specify a set of translated urls to use instead of downloading the artifacts * from their original locations. The given set will be extended with already * built artifacts from the maven project. @@ -335,6 +343,7 @@ public class AssemblyMojo extends MojoSupport { builder.propertyEdits(edits); } } + builder.pidsToExtract(pidsToExtract); Map<String, String> urls = new HashMap<>(); List<Artifact> artifacts = new ArrayList<>(project.getAttachedArtifacts());
