karaf-3982: reusable bits and pieces that do property editing.
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/04840c83 Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/04840c83 Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/04840c83 Branch: refs/heads/master Commit: 04840c8316ab475f1f5d0fc92421aab28ca1ce7e Parents: 0077f18 Author: Benson Margulies <[email protected]> Authored: Wed Sep 9 09:49:32 2015 -0400 Committer: Benson Margulies <[email protected]> Committed: Wed Sep 9 09:49:32 2015 -0400 ---------------------------------------------------------------------- tooling/pom.xml | 1 + tooling/utils/pom.xml | 74 +++++++++++++ .../org/apache/karaf/tools/utils/JoinUtil.java | 59 +++++++++++ .../apache/karaf/tools/utils/JoinableValue.java | 25 +++++ .../tools/utils/KarafPropertiesEditor.java | 78 ++++++++++++++ .../karaf/tools/utils/KarafPropertiesFile.java | 104 +++++++++++++++++++ tooling/utils/src/main/mdo/edits.mdo | 100 ++++++++++++++++++ .../tools/utils/KarafPropertiesEditorTest.java | 77 ++++++++++++++ .../org/apache/karaf/tools/utils/test-edits.xml | 29 ++++++ 9 files changed, 547 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/04840c83/tooling/pom.xml ---------------------------------------------------------------------- diff --git a/tooling/pom.xml b/tooling/pom.xml index 4ed31a5..1aac831 100644 --- a/tooling/pom.xml +++ b/tooling/pom.xml @@ -34,6 +34,7 @@ <name>Apache Karaf :: Tooling</name> <modules> + <module>utils</module> <module>karaf-services-maven-plugin</module> <module>karaf-maven-plugin</module> </modules> http://git-wip-us.apache.org/repos/asf/karaf/blob/04840c83/tooling/utils/pom.xml ---------------------------------------------------------------------- diff --git a/tooling/utils/pom.xml b/tooling/utils/pom.xml new file mode 100644 index 0000000..ece56ce --- /dev/null +++ b/tooling/utils/pom.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <!-- + + 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. + --> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>tooling</artifactId> + <version>4.0.2-SNAPSHOT</version> + </parent> + + <artifactId>org.apache.karaf.tools.utils</artifactId> + <packaging>bundle</packaging> + <name>Apache Karaf :: Tooling :: Utils</name> + <description>Karaf tooling resuable elsewhere (such as the Karaf pax-exam container).</description> + <dependencies> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>16.0.1</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.modello</groupId> + <artifactId>modello-maven-plugin</artifactId> + <version>1.8.3</version> + <executions> + <execution> + <goals> + <goal>java</goal> + <goal>stax-reader</goal> + <goal>stax-writer</goal> + <goal>xsd</goal> + </goals> + </execution> + </executions> + <configuration> + <models> + <model>src/main/mdo/edits.mdo</model> + </models> + <useJava5>true</useJava5> + <version>1.0.0</version> + </configuration> + </plugin> + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/karaf/blob/04840c83/tooling/utils/src/main/java/org/apache/karaf/tools/utils/JoinUtil.java ---------------------------------------------------------------------- diff --git a/tooling/utils/src/main/java/org/apache/karaf/tools/utils/JoinUtil.java b/tooling/utils/src/main/java/org/apache/karaf/tools/utils/JoinUtil.java new file mode 100644 index 0000000..ee18cc9 --- /dev/null +++ b/tooling/utils/src/main/java/org/apache/karaf/tools/utils/JoinUtil.java @@ -0,0 +1,59 @@ +/* + * 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.tools.utils; + +/** + * Join strings with commas. This has some reflection to be usable with + * the pax-exam + */ +public class JoinUtil { + + private JoinUtil() { + } + + public static String join(String... options) { + return join((Object[])options); + } + + public static String join(Object[] options) { + StringBuilder buffer = new StringBuilder(); + for (Object option : options) { + String value = getStringValue(option); + if (buffer.length() > 0 && !value.startsWith(",")) { + buffer.append(","); + } + buffer.append(value); + } + return buffer.toString(); + } + + @SuppressWarnings("unchecked") + private static String getStringValue(Object option) { + if (option == null) { + return ""; + } + else if (option instanceof String) { + return (String)option; + } + else if (option instanceof JoinableValue) { + return ((JoinableValue<String>)option).getValue(); + } + else { + return ""; + } + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/04840c83/tooling/utils/src/main/java/org/apache/karaf/tools/utils/JoinableValue.java ---------------------------------------------------------------------- diff --git a/tooling/utils/src/main/java/org/apache/karaf/tools/utils/JoinableValue.java b/tooling/utils/src/main/java/org/apache/karaf/tools/utils/JoinableValue.java new file mode 100644 index 0000000..421352b --- /dev/null +++ b/tooling/utils/src/main/java/org/apache/karaf/tools/utils/JoinableValue.java @@ -0,0 +1,25 @@ +/* + * 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.tools.utils; + +/** + * General definition of an object that specifies a value for Join. + */ +public interface JoinableValue<T> { + T getValue(); +} http://git-wip-us.apache.org/repos/asf/karaf/blob/04840c83/tooling/utils/src/main/java/org/apache/karaf/tools/utils/KarafPropertiesEditor.java ---------------------------------------------------------------------- diff --git a/tooling/utils/src/main/java/org/apache/karaf/tools/utils/KarafPropertiesEditor.java b/tooling/utils/src/main/java/org/apache/karaf/tools/utils/KarafPropertiesEditor.java new file mode 100644 index 0000000..15f0c20 --- /dev/null +++ b/tooling/utils/src/main/java/org/apache/karaf/tools/utils/KarafPropertiesEditor.java @@ -0,0 +1,78 @@ +/* + * 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.tools.utils; + +import org.apache.karaf.tools.utils.model.KarafPropertyEdit; +import org.apache.karaf.tools.utils.model.KarafPropertyEdits; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Apply a set of edits, reading files from a stock etc dir. + */ +public class KarafPropertiesEditor { + private File inputEtc; + private File outputEtc; + private KarafPropertyEdits edits; + + public KarafPropertiesEditor setInputEtc(File inputEtc) { + this.inputEtc = inputEtc; + return this; + } + + public KarafPropertiesEditor setOutputEtc(File outputEtc) { + this.outputEtc = outputEtc; + return this; + } + + public KarafPropertiesEditor setEdits(KarafPropertyEdits edits) { + this.edits = edits; + return this; + } + + public void run() throws IOException { + + Map<String, List<KarafPropertyEdit>> editsByFile = new HashMap<>(); + + // organize edits by file. + for (KarafPropertyEdit edit : edits.getEdits()) { + List<KarafPropertyEdit> thisFileEdits = editsByFile.get(edit.getFile()); + if (thisFileEdits == null) { + thisFileEdits = new ArrayList<>(); + editsByFile.put(edit.getFile(), thisFileEdits); + } + thisFileEdits.add(edit); + } + + for (Map.Entry<String, List<KarafPropertyEdit>> fileOps : editsByFile.entrySet()) { + File input = new File(inputEtc, fileOps.getKey()); + KarafPropertiesFile propsFile = new KarafPropertiesFile(input); + propsFile.load(); + List<KarafPropertyEdit> edits = fileOps.getValue(); + for (KarafPropertyEdit edit : edits) { + propsFile.apply(edit); + } + File outputFile = new File(outputEtc, fileOps.getKey()); + propsFile.store(outputFile); + } + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/04840c83/tooling/utils/src/main/java/org/apache/karaf/tools/utils/KarafPropertiesFile.java ---------------------------------------------------------------------- diff --git a/tooling/utils/src/main/java/org/apache/karaf/tools/utils/KarafPropertiesFile.java b/tooling/utils/src/main/java/org/apache/karaf/tools/utils/KarafPropertiesFile.java new file mode 100644 index 0000000..ef75d75 --- /dev/null +++ b/tooling/utils/src/main/java/org/apache/karaf/tools/utils/KarafPropertiesFile.java @@ -0,0 +1,104 @@ +/* + * 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.tools.utils; + +import org.apache.commons.io.FileUtils; +import org.apache.karaf.tools.utils.model.KarafPropertyEdit; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +public class KarafPropertiesFile { + + private final Properties properties; + private final File propertyFile; + + public KarafPropertiesFile(File karafHome, String location) { + this(homedPropFile(karafHome, location)); + } + + public KarafPropertiesFile(File propertyFile) { + this.propertyFile = propertyFile; + properties = new Properties(); + } + + private static File homedPropFile(File karafHome, String location) { + File propFile; + if (location.startsWith("/")) { + propFile = new File(karafHome + location); + } else { + propFile = new File(karafHome + "/" + location); + } + return propFile; + } + + public void load() throws IOException { + if (!propertyFile.exists()) { + return; + } + properties.load(new FileInputStream(propertyFile)); + } + + public void put(String key, String value) { + properties.put(key, value); + } + + public void extend(String key, String value) { + if (properties.get(key) == null) { + properties.put(key, value); + return; + } + properties.put(key, JoinUtil.join((String)properties.get(key), value)); + } + + public void apply(KarafPropertyEdit editSpec) { + if ("extend".equals(editSpec.getOperation())) { + extend(editSpec.getKey(), editSpec.getValue()); + } else if ("put".equals(editSpec.getOperation())) { + put(editSpec.getKey(), editSpec.getValue()); + } else { + throw new IllegalArgumentException("Operation must be 'extend' or 'put', not " + editSpec.getOperation()); + } + } + + public String get(String key) { + return properties.getProperty(key); + } + + public void store() throws IOException { + store(propertyFile); + } + + public void store(File destinationFile) throws IOException { + try (FileOutputStream outputStream = new FileOutputStream(destinationFile)) { + properties.store(outputStream, String.format("Modified by %s", getClass().getName())); + } + } + + public void replace(File source) { + try { + FileUtils.copyFile(source, propertyFile); + } + catch (IOException e) { + throw new IllegalStateException(String.format("Failed to replace %s", propertyFile.getAbsolutePath()), e); + } + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/04840c83/tooling/utils/src/main/mdo/edits.mdo ---------------------------------------------------------------------- diff --git a/tooling/utils/src/main/mdo/edits.mdo b/tooling/utils/src/main/mdo/edits.mdo new file mode 100644 index 0000000..5dfc48e --- /dev/null +++ b/tooling/utils/src/main/mdo/edits.mdo @@ -0,0 +1,100 @@ +<?xml version="1.0"?> +<!-- + + 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. +--> +<model xmlns="http://modello.codehaus.org/MODELLO/1.4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://modello.codehaus.org/MODELLO/1.4.0 http://modello.codehaus.org/xsd/modello-1.4.0.xsd" + xml.namespace="http://karaf.apache.org/tools/property-edits/${version}" + xml.schemaLocation="http://karaf.apache.org/xsd/property-edits-${version}.xsd"> + <id>karaf-property-instructions-model</id> + <name>KarafPropertyInstructionsModel</name> + <description> + Specifications for how to modify Karaf property files to customize an installation. + </description> + <defaults> + <default> + <key>package</key> + <value>org.apache.karaf.tools.utils.model</value> + </default> + </defaults> + <classes> + <class rootElement="true" xml.tagName="property-edits"> + <description> + A collection instructions. Each instruction is either a 'put' or an 'edit', + and calls out a file and a series of name/value pairs. + </description> + <name>KarafPropertyEdits</name> + <version>1.0.0+</version> + <fields> + <field> + <name>edits</name> + <version>1.0.0+</version> + <association> + <type>KarafPropertyEdit</type> + <multiplicity>*</multiplicity> + </association> + <description>The list of edits to perform.</description> + </field> + </fields> + </class> + <class xml.tagName="edit"> + <name>KarafPropertyEdit</name> + <version>1.0.0+</version> + <description> + A single property edit. + </description> + <fields> + <field> + <name>operation</name> + <version>1.0.0+</version> + <type>String</type> + <description> + 'extend' to extend the existing value, 'put' to replace. + </description> + <required>true</required> + </field> + <field> + <name>file</name> + <version>1.0.0+</version> + <type>String</type> + <description> + The pathname, relative to the the Karaf etc directory, of the file to manipulate. + </description> + <required>true</required> + </field> + <field> + <name>key</name> + <version>1.0.0+</version> + <type>String</type> + <description> + The property key. + </description> + <required>true</required> + </field> + <field> + <name>value</name> + <version>1.0.0+</version> + <type>String</type> + <description> + The property value. + </description> + <required>true</required> + </field> + </fields> + </class> + </classes> +</model> http://git-wip-us.apache.org/repos/asf/karaf/blob/04840c83/tooling/utils/src/test/java/org/apache/karaf/tools/utils/KarafPropertiesEditorTest.java ---------------------------------------------------------------------- diff --git a/tooling/utils/src/test/java/org/apache/karaf/tools/utils/KarafPropertiesEditorTest.java b/tooling/utils/src/test/java/org/apache/karaf/tools/utils/KarafPropertiesEditorTest.java new file mode 100644 index 0000000..e49226e --- /dev/null +++ b/tooling/utils/src/test/java/org/apache/karaf/tools/utils/KarafPropertiesEditorTest.java @@ -0,0 +1,77 @@ +/* + * 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.tools.utils; + +import com.google.common.io.Resources; +import org.apache.karaf.tools.utils.model.KarafPropertyEdits; +import org.apache.karaf.tools.utils.model.io.stax.KarafPropertyInstructionsModelStaxReader; +import org.junit.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; + +/** + * Test the property editing system. + * See src/test/resources/.../test-edit.xml. + */ +public class KarafPropertiesEditorTest { + private static final String ETC_TO_START_WITH = "../../main/src/test/resources/test-karaf-home/etc/"; + + @Test + public void onceOver() throws Exception { + KarafPropertyInstructionsModelStaxReader kipmsr = new KarafPropertyInstructionsModelStaxReader(); + URL editsUrl = Resources.getResource(KarafPropertiesEditorTest.class, "test-edits.xml"); + KarafPropertyEdits edits; + try (InputStream editsStream = Resources.asByteSource(editsUrl).openStream()) { + edits = kipmsr.read(editsStream, true); + } + + Path path = FileSystems.getDefault().getPath("target"); + Path outputEtc = Files.createTempDirectory(path, "test-etc"); + + KarafPropertiesEditor editor = new KarafPropertiesEditor(); + editor.setInputEtc(new File(ETC_TO_START_WITH)) + .setOutputEtc(outputEtc.toFile()) + .setEdits(edits); + editor.run(); + + File resultConfigProps = new File(outputEtc.toFile(), "config.properties"); + Properties properties = new Properties(); + try (InputStream resultInputStream = new FileInputStream(resultConfigProps)) { + properties.load(resultInputStream); + } + assertEquals("equinox", properties.getProperty("karaf.framework")); + assertEquals("root,toor", properties.getProperty("karaf.name")); + + resultConfigProps = new File(outputEtc.toFile(), "jre.properties"); + try (InputStream resultInputStream = new FileInputStream(resultConfigProps)) { + properties.load(resultInputStream); + } + + assertEquals("This is the cereal: shot from guns", properties.getProperty("test-add-one")); + assertEquals("This is the gun that shoots cereal", properties.getProperty("test-add-two")); + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/04840c83/tooling/utils/src/test/resources/org/apache/karaf/tools/utils/test-edits.xml ---------------------------------------------------------------------- diff --git a/tooling/utils/src/test/resources/org/apache/karaf/tools/utils/test-edits.xml b/tooling/utils/src/test/resources/org/apache/karaf/tools/utils/test-edits.xml new file mode 100644 index 0000000..2aa8fca --- /dev/null +++ b/tooling/utils/src/test/resources/org/apache/karaf/tools/utils/test-edits.xml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<property-edits xmlns="http://karaf.apache.org/tools/property-edits/1.0.0"> + <edits> + <edit> + <file>config.properties</file> + <operation>put</operation> + <key>karaf.framework</key> + <value>equinox</value> + </edit> + <edit> + <file>config.properties</file> + <operation>extend</operation> + <key>karaf.name</key> + <value>toor</value> + </edit> + <edit> + <file>jre.properties</file> + <operation>put</operation> + <key>test-add-one</key> + <value>This is the cereal: shot from guns</value> + </edit> + <edit> + <file>jre.properties</file> + <operation>extend</operation> + <key>test-add-two</key> + <value>This is the gun that shoots cereal</value> + </edit> + </edits> +</property-edits>
