Command to compare version upgrades
diff --git a/pom.xml b/pom.xml
index 72fdb48..e4346f1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -288,17 +288,17 @@
<dependency>
<groupId>org.tomitribe</groupId>
<artifactId>tomitribe-crest</artifactId>
- <version>0.15</version>
+ <version>0.19</version>
</dependency>
<dependency>
<groupId>org.tomitribe</groupId>
<artifactId>swizzle</artifactId>
- <version>1.1</version>
+ <version>1.3</version>
</dependency>
<dependency>
<groupId>org.tomitribe</groupId>
<artifactId>tomitribe-util</artifactId>
- <version>1.3.18</version>
+ <version>1.4.4</version>
</dependency>
<dependency>
<groupId>org.tomitribe.jamira</groupId>
diff --git a/src/main/java/org/apache/openejb/tools/release/Loader.java b/src/main/java/org/apache/openejb/tools/release/Loader.java
index 41a927e..6ab7238 100644
--- a/src/main/java/org/apache/openejb/tools/release/Loader.java
+++ b/src/main/java/org/apache/openejb/tools/release/Loader.java
@@ -17,6 +17,7 @@
package org.apache.openejb.tools.release;
+import org.apache.openejb.tools.release.cmd.AnalyzeUpgrades;
import org.apache.openejb.tools.release.cmd.Dist;
import org.apache.openejb.tools.release.cmd.ReleaseNotes;
@@ -30,7 +31,7 @@
return Arrays.asList(
Dist.class,
ReleaseNotes.class,
- Object.class
+ AnalyzeUpgrades.class
).iterator();
}
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/openejb/tools/release/cmd/AnalyzeUpgrades.java b/src/main/java/org/apache/openejb/tools/release/cmd/AnalyzeUpgrades.java
new file mode 100644
index 0000000..c460f38
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/cmd/AnalyzeUpgrades.java
@@ -0,0 +1,139 @@
+/*
+ * 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.openejb.tools.release.cmd;
+
+import lombok.Data;
+import org.apache.openejb.tools.release.maven.pom.Dependency;
+import org.apache.openejb.tools.release.maven.pom.PomParser;
+import org.apache.openejb.tools.release.maven.pom.Project;
+import org.apache.openejb.tools.release.util.Exec;
+import org.tomitribe.crest.api.Command;
+import org.tomitribe.crest.api.table.Table;
+import org.tomitribe.util.Files;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+@Command("upgrades")
+public class AnalyzeUpgrades {
+
+ /**
+ * Compares the version upgrades from one version to another using
+ * the bom files for tomee-webprofile, tomee-microprofile, tomee-plus,
+ * and tomee-plume
+ * @param from a previous TomEE version
+ * @param to a newer TomEE version
+ */
+ @Table(fields = "artifactId from to")
+ @Command("compare")
+ public Stream<Upgrade> compare(final String from, final String to) {
+
+ final Map<String, Dependency> previous = getDependencies(from);
+ final Map<String, Dependency> current = getDependencies(to);
+
+ return current.keySet().stream()
+ .sorted()
+ .filter(previous::containsKey)
+ .filter(s -> !previous.get(s).getVersion().equals(current.get(s).getVersion()))
+ .map(s -> new Upgrade(previous.get(s), current.get(s)));
+ }
+
+ private Map<String, Dependency> getDependencies(final String version) {
+ final Map<String, Dependency> map = new HashMap<>();
+ Stream.of("tomee-webprofile", "tomee-microprofile", "tomee-plus", "tomee-plume")
+ .map(s -> getDependencies(s, version))
+ .flatMap(Collection::stream)
+ .forEach(dependency -> map.put(dependency.getGroupId() + ":" + dependency.getArtifactId(), dependency));
+ return map;
+ }
+
+ private List<Dependency> getDependencies(final String artifactId, final String version) {
+ final File pom = resolve("org.apache.tomee.bom", artifactId, version, "pom");
+
+ final Project project = PomParser.parse(pom);
+ return project.getDependencies();
+ }
+
+ private File resolve(final String groupId, final String artifactId, final String version, final String packaging) {
+ try {
+ return mvn(groupId, artifactId, version, packaging);
+ } catch (IllegalStateException e) {
+ Exec.exec("mvn", "org.apache.maven.plugins:maven-dependency-plugin:3.3.0:get",
+ "-DgroupId=" + groupId,
+ "-DartifactId=" + artifactId,
+ "-Dversion=" + version,
+ "-Dpackaging=" + packaging
+ );
+ return mvn(groupId, artifactId, version, packaging);
+ }
+ }
+
+ public static File mvn(final String group, final String artifact, final String version, final String packaging) {
+ final File repository = repository();
+
+ // org/apache/tomee/tomee-util/7.1.0/tomee-util-7.1.0.jar
+ final File archive = Files.file(
+ repository,
+ group.replace('.', '/'),
+ artifact,
+ version,
+ String.format("%s-%s.%s", artifact, version, packaging));
+
+ Files.exists(archive);
+ Files.file(archive);
+ Files.readable(archive);
+ return archive;
+ }
+
+ private static File repository() {
+ final List<String> path = Arrays.asList(System.getProperty("user.home"), ".m2", "repository");
+
+ File file = null;
+ ;
+ for (final String part : path) {
+ if (part == null) file = new File(part);
+ else file = new File(file, part);
+ Files.exists(file);
+ Files.dir(file);
+ }
+ return file;
+ }
+
+ @Data
+ public static class Upgrade {
+ private final String groupId;
+ private final String artifactId;
+ private final String from;
+ private final String to;
+
+ public Upgrade(final Dependency from, final Dependency to) {
+ this.groupId = to.getGroupId();
+ this.artifactId = to.getArtifactId();
+ this.from = from.getVersion();
+ this.to = to.getVersion();
+ }
+ }
+
+ public static void main(String[] args) {
+ new AnalyzeUpgrades().compare("9.0.0-M8", "9.0.0-M9-SNAPSHOT").forEach(System.out::println);
+ }
+}
diff --git a/src/main/java/org/apache/openejb/tools/release/maven/pom/Coordinates.java b/src/main/java/org/apache/openejb/tools/release/maven/pom/Coordinates.java
new file mode 100644
index 0000000..ced020c
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/maven/pom/Coordinates.java
@@ -0,0 +1,29 @@
+/*
+ * 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.openejb.tools.release.maven.pom;
+
+public interface Coordinates {
+ String getGroupId();
+
+ String getArtifactId();
+
+ String getVersion();
+
+ default String gav() {
+ return String.format("%s:%s:%s", getGroupId(), getArtifactId(), getVersion());
+ }
+}
diff --git a/src/main/java/org/apache/openejb/tools/release/maven/pom/Dependency.java b/src/main/java/org/apache/openejb/tools/release/maven/pom/Dependency.java
new file mode 100644
index 0000000..11e7339
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/maven/pom/Dependency.java
@@ -0,0 +1,55 @@
+/*
+ * 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.openejb.tools.release.maven.pom;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@XmlRootElement(name = "dependency")
+@XmlAccessorType(XmlAccessType.FIELD)
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+public class Dependency implements Coordinates {
+ @XmlElement(name = "groupId")
+ private String groupId;
+
+ @XmlElement(name = "artifactId")
+ private String artifactId;
+
+ @XmlElement(name = "version")
+ private String version;
+
+ @XmlElement(name = "scope")
+ private String scope;
+
+ @XmlElement(name = "classifier")
+ private String classifier;
+
+ @XmlElement(name = "type")
+ private String type;
+}
diff --git a/src/main/java/org/apache/openejb/tools/release/maven/pom/MavenPomException.java b/src/main/java/org/apache/openejb/tools/release/maven/pom/MavenPomException.java
new file mode 100644
index 0000000..9f84c3f
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/maven/pom/MavenPomException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.openejb.tools.release.maven.pom;
+
+public class MavenPomException extends IllegalStateException {
+ public MavenPomException(final Exception e) {
+ super(e);
+ }
+
+ public MavenPomException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/apache/openejb/tools/release/maven/pom/Parent.java b/src/main/java/org/apache/openejb/tools/release/maven/pom/Parent.java
new file mode 100644
index 0000000..e3e10cc
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/maven/pom/Parent.java
@@ -0,0 +1,49 @@
+/*
+ * 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.openejb.tools.release.maven.pom;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@XmlRootElement(name = "parent")
+@XmlAccessorType(XmlAccessType.FIELD)
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+public class Parent implements Coordinates {
+ @XmlElement(name = "groupId")
+ private String groupId;
+
+ @XmlElement(name = "artifactId")
+ private String artifactId;
+
+ @XmlElement(name = "version")
+ private String version;
+
+ @XmlElement(name = "relativePath")
+ private String relativePath;
+}
diff --git a/src/main/java/org/apache/openejb/tools/release/maven/pom/PomParser.java b/src/main/java/org/apache/openejb/tools/release/maven/pom/PomParser.java
new file mode 100644
index 0000000..ad174de
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/maven/pom/PomParser.java
@@ -0,0 +1,175 @@
+/*
+ * 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.openejb.tools.release.maven.pom;
+
+import org.apache.openejb.tools.release.util.JsonMarshalling;
+import org.tomitribe.swizzle.stream.StreamBuilder;
+import org.tomitribe.util.IO;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+public class PomParser {
+
+ public static Project parse(final URL url) {
+ try {
+ final String xml = IO.slurp(url);
+ return parse(xml);
+ } catch (IOException e) {
+ throw new MavenPomException(e);
+ }
+ }
+
+ public static Project parse(final File file) {
+ try {
+ final String xml = IO.slurp(file);
+ return parse(xml);
+ } catch (IOException e) {
+ throw new MavenPomException(e);
+ }
+ }
+
+ public static Project parse(final String xml) {
+ try {
+ final JAXBContext context = JAXBContext.newInstance(Project.class);
+ final Unmarshaller unmarshaller = context.createUnmarshaller();
+
+ final Project project = (Project) unmarshaller.unmarshal(IO.read(trimPomXml(xml)));
+
+ return interpolate(project);
+ } catch (Exception e) {
+ throw new MavenPomException(e);
+ }
+ }
+
+ /**
+ * JAXB is unforgiving if you do not model the xml schema in 100% entirety.
+ *
+ * We only want a few key elements and do not need all the rest, so we use this method
+ * to strip out all the stuff we do not need, leaving only what we can unmarshal.
+ */
+ private static String trimPomXml(final String rawXml) throws IOException, ParserConfigurationException, SAXException, TransformerException {
+
+ /*
+ * Read the xml into a dom and strip out the parts we do not need
+ */
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ final DocumentBuilder builder = factory.newDocumentBuilder();
+
+ final Document document = builder.parse(IO.read(rawXml.trim()));
+ final Element documentElement = document.getDocumentElement();
+
+ /*
+ * Strip off the namespace. Some people use it, some do not. JAXB is incapable of
+ * being flexible on this so we normalize everything to not have the namespace.
+ */
+ final NamedNodeMap attributes = documentElement.getAttributes();
+ while (attributes.getLength() > 0) {
+ final Node item = attributes.item(0);
+ attributes.removeNamedItem(item.getNodeName());
+ }
+
+ /*
+ * These are the only elements we care about
+ */
+ final Predicate<Node> wanted = element("parent")
+ .or(element("groupId"))
+ .or(element("artifactId"))
+ .or(element("version"))
+ .or(element("properties"))
+ .or(element("dependencies"));
+
+ /*
+ * Remove anything but the wanted elements
+ */
+ nodes(documentElement).stream()
+ .filter(wanted.negate())
+ .forEach(documentElement::removeChild);
+
+ /*
+ * Long-winded boilerplate code to write the DOM back out as xml
+ */
+ final TransformerFactory tf = TransformerFactory.newInstance();
+ final Transformer trans = tf.newTransformer();
+ final StringWriter sw = new StringWriter();
+ trans.transform(new DOMSource(document), new StreamResult(sw));
+ return sw.toString();
+ }
+
+ private static Predicate<Node> element(final String name) {
+ return node -> node.getNodeName().equals(name);
+ }
+
+ private static List<Node> nodes(final Element documentElement) {
+ final List<Node> nodes = new ArrayList<Node>();
+ final NodeList childNodes = documentElement.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ final Node item = childNodes.item(i);
+ nodes.add(item);
+ }
+ return nodes;
+ }
+
+ private static Project interpolate(final Project project) throws IOException {
+ if (project.getProperties() == null) return project;
+ final String actualJson = JsonMarshalling.toFormattedJson(project);
+ final Map<String, String> properties = project.getProperties();
+
+ final String interpolated = interpolate(actualJson, properties);
+ return JsonMarshalling.unmarshal(Project.class, interpolated);
+ }
+
+ public static String interpolate(final String content, final Map<String, String> properties) throws IOException {
+ String previous, current = content;
+
+ do {
+ previous = current;
+ final InputStream inputStream = StreamBuilder.create(IO.read(current))
+ .replace("${", "}", s -> {
+ final String value = properties.get(s);
+ if (value == null) return "${" + s + "}";
+ return value;
+ }).get();
+ current = IO.slurp(inputStream);
+ } while (!current.equals(previous));
+
+ return current;
+ }
+}
diff --git a/src/main/java/org/apache/openejb/tools/release/maven/pom/Project.java b/src/main/java/org/apache/openejb/tools/release/maven/pom/Project.java
new file mode 100644
index 0000000..53c436a
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/maven/pom/Project.java
@@ -0,0 +1,80 @@
+/*
+ * 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.openejb.tools.release.maven.pom;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.tomitribe.util.IO;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@XmlRootElement(name = "project")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Project implements Coordinates {
+
+ @XmlTransient
+ private File file;
+
+ @XmlElement(name = "parent")
+ private Parent parent;
+
+ @XmlElement(name = "groupId")
+ private String groupId;
+
+ @XmlElement(name = "artifactId")
+ private String artifactId;
+
+ @XmlElement(name = "version")
+ private String version;
+
+ @XmlElement(name = "properties")
+ @XmlJavaTypeAdapter(PropertiesAdapter.class)
+ private Map<String, String> properties;
+
+ @XmlElementWrapper(name = "dependencies")
+ @XmlElement(name = "dependency")
+ private List<Dependency> dependencies = new ArrayList<>();
+
+
+ public static Project parse(final File file) {
+ try {
+ final Project project = PomParser.parse(IO.slurp(file));
+ project.setFile(file);
+ return project;
+ } catch (IOException e) {
+ throw new MavenPomException("Unable to read file " + file.getAbsolutePath(), e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/openejb/tools/release/maven/pom/Properties.java b/src/main/java/org/apache/openejb/tools/release/maven/pom/Properties.java
new file mode 100644
index 0000000..a2627c5
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/maven/pom/Properties.java
@@ -0,0 +1,43 @@
+/*
+ * 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.openejb.tools.release.maven.pom;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.w3c.dom.Node;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAnyElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.List;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@XmlRootElement(name = "properties")
+@XmlAccessorType(XmlAccessType.FIELD)
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+public class Properties {
+
+ @XmlAnyElement(lax = true)
+ protected List<Node> any;
+}
diff --git a/src/main/java/org/apache/openejb/tools/release/maven/pom/PropertiesAdapter.java b/src/main/java/org/apache/openejb/tools/release/maven/pom/PropertiesAdapter.java
new file mode 100644
index 0000000..8a52376
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/maven/pom/PropertiesAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.openejb.tools.release.maven.pom;
+
+import org.w3c.dom.Node;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class PropertiesAdapter extends XmlAdapter<Properties, Map<String, String>> {
+
+ @Override
+ public Map<String, String> unmarshal(final Properties v) throws Exception {
+ final Map<String, String> map = new LinkedHashMap<>();
+
+ for (final Node node : v.getAny()) {
+ final String name = node.getNodeName();
+ final String value = node.getTextContent();
+ map.put(name, value);
+ }
+
+ return map;
+ }
+
+ @Override
+ public Properties marshal(final Map<String, String> v) throws Exception {
+ return null;
+ }
+}
diff --git a/src/main/java/org/apache/openejb/tools/release/util/JsonMarshalling.java b/src/main/java/org/apache/openejb/tools/release/util/JsonMarshalling.java
new file mode 100644
index 0000000..1111195
--- /dev/null
+++ b/src/main/java/org/apache/openejb/tools/release/util/JsonMarshalling.java
@@ -0,0 +1,81 @@
+/*
+ * 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.openejb.tools.release.util;
+
+import org.tomitribe.util.IO;
+import org.tomitribe.util.PrintString;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static javax.json.bind.JsonbConfig.FORMATTING;
+
+public class JsonMarshalling {
+ private JsonMarshalling() {
+ }
+
+ public static <JsonbType> JsonbType unmarshal(final Class<JsonbType> type, final File jsonFile) {
+ try {
+ final Jsonb jsonb = JsonbBuilder.create();
+ try (final InputStream stream = IO.read(jsonFile)) {
+ return jsonb.fromJson(stream, type);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException(String.format("Unable to unmarshal %s from file %s", type.getSimpleName(), jsonFile.getAbsolutePath()), e);
+ }
+ }
+
+ public static <JsonbType> JsonbType unmarshal(final Class<JsonbType> type, final String json) {
+ try {
+ final Jsonb jsonb = JsonbBuilder.create();
+ try (final InputStream stream = IO.read(json)) {
+ return jsonb.fromJson(stream, type);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException(String.format("Unable to unmarshal %s", type.getSimpleName()), e);
+ }
+ }
+
+ public static <JsonObject> File toFormattedJson(final JsonObject jsonObject, final File file) {
+ try {
+ final JsonbConfig config = new JsonbConfig();
+ config.setProperty(FORMATTING, true);
+ final Jsonb jsonb = JsonbBuilder.create(config);
+ try (final OutputStream write = IO.write(file)) {
+ jsonb.toJson(jsonObject, write);
+ }
+ return file;
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public static <JsonObject> String toFormattedJson(final JsonObject jsonObject) {
+ final JsonbConfig config = new JsonbConfig();
+ config.setProperty(FORMATTING, true);
+ final Jsonb jsonb = JsonbBuilder.create(config);
+ final PrintString out = new PrintString();
+ jsonb.toJson(jsonObject, out);
+ return out.toString();
+ }
+}