This is an automated email from the ASF dual-hosted git repository. fmariani pushed a commit to branch camel-spring-boot-4.0.0-branch in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
commit 747e906355a060363b5e08ab84338081cf650363 Author: Grzegorz Grzybek <[email protected]> AuthorDate: Fri Mar 17 14:48:06 2023 +0100 [CSB-1569][CSB-1570] Add patch-maven-plugin from Fuse 7 to handle Camel on Spring Boot product (3.18) (cherry picked from commit 972cc3a56f0ae8f92dcdf837e8ced8048846435c) [CSB-1569][CSB-1570] Prepare template patch metadata and repository assembly (cherry picked from commit 9d95a975032bb69b750f8c8b652a6759c0252b38) [CSB-1569][CSB-1570] No need to have ZIP wagon at all, because the offline patch is unzipped to tmp directory (cherry picked from commit 736c91ae1ab6c9be8205753f5dc5b8788e18ee31) [CSB-1569][CSB-1570] Remove the test that relied on removed ZIP repository layout (cherry picked from commit 5ea7acfb50e9eea8047fdcb5aca743d3d10c3ee2) --- .../src/main/resources/archetype-resources/pom.xml | 9 + maintenance/pom.xml | 40 ++ .../pom.xml | 71 ++ .../src/main/resources/metadata.xml | 33 + .../pom.xml | 107 +++ .../src/main/descriptors/repository.xml | 48 ++ pom.xml | 34 +- tooling/pom.xml | 3 + tooling/redhat-camel-spring-boot-bom/pom.xml | 170 +++++ tooling/redhat-patch-maven-plugin/pom.xml | 193 ++++++ .../it/cve-dependency-management/module1/pom.xml | 39 ++ .../src/it/cve-dependency-management/pom.xml | 65 ++ .../patch/SecureDependencyManagement.java | 742 +++++++++++++++++++++ .../springboot/patch/extensions/ZipWagon.java | 296 ++++++++ .../patch/model/AffectedArtifactSpec.java | 121 ++++ .../apache/camel/springboot/patch/model/CVE.java | 81 +++ .../apache/camel/springboot/patch/model/Fix.java | 67 ++ .../springboot/patch/model/PatchMetadata.java | 73 ++ .../springboot/patch/model/ProductVersion.java | 102 +++ .../src/main/resources/patch-metadata.xsd | 78 +++ .../src/main/resources/plugin.properties | 28 + .../apache/camel/springboot/patch/MavenTest.java | 266 ++++++++ .../camel/springboot/patch/ZipWagonProvider.java | 37 + .../src/test/resources/log4j2.properties | 28 + .../src/test/resources/patch-1.zip | Bin 0 -> 1527 bytes 25 files changed, 2729 insertions(+), 2 deletions(-) diff --git a/archetypes/camel-archetype-spring-boot/src/main/resources/archetype-resources/pom.xml b/archetypes/camel-archetype-spring-boot/src/main/resources/archetype-resources/pom.xml index b0762cb1028..fadbaa6e821 100644 --- a/archetypes/camel-archetype-spring-boot/src/main/resources/archetype-resources/pom.xml +++ b/archetypes/camel-archetype-spring-boot/src/main/resources/archetype-resources/pom.xml @@ -127,6 +127,15 @@ <artifactId>maven-surefire-plugin</artifactId> <version>${surefire.plugin.version}</version> </plugin> + <plugin> + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>patch-maven-plugin</artifactId> + <version>${camel-spring-boot-version}</version> + <extensions>true</extensions> + <configuration> + <skip>false</skip> + </configuration> + </plugin> </plugins> </build> diff --git a/maintenance/pom.xml b/maintenance/pom.xml new file mode 100644 index 00000000000..db9bf720ea4 --- /dev/null +++ b/maintenance/pom.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2005-2023 Red Hat, Inc. + + Red Hat 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. + +--> +<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"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>spring-boot-parent</artifactId> + <version>4.0.0-SNAPSHOT</version> + <relativePath>../parent/pom.xml</relativePath> + </parent> + + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>redhat-camel-spring-boot-maintenance</artifactId> + <packaging>pom</packaging> + <name>Camel SB Maintenance</name> + + <modules> + <module>redhat-camel-spring-boot-patch-metadata</module> + <module>redhat-camel-spring-boot-patch-repository</module> + </modules> + +</project> diff --git a/maintenance/redhat-camel-spring-boot-patch-metadata/pom.xml b/maintenance/redhat-camel-spring-boot-patch-metadata/pom.xml new file mode 100644 index 00000000000..9c0e5867ab3 --- /dev/null +++ b/maintenance/redhat-camel-spring-boot-patch-metadata/pom.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2005-2023 Red Hat, Inc. + + Red Hat 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. + +--> +<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"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>redhat-camel-spring-boot-maintenance</artifactId> + <version>4.0.0-SNAPSHOT</version> + </parent> + + <artifactId>redhat-camel-spring-boot-patch-metadata</artifactId> + <packaging>jar</packaging> + <name>Camel SB Patch Metadata</name> + + <properties> + <!-- A list of versions used to filter patch metadata.xml --> + <patch.camel-version>4.0.0</patch.camel-version> + </properties> + + <build> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + </resource> + </resources> + + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>attach-artifacts</id> + <phase>package</phase> + <goals> + <goal>attach-artifact</goal> + </goals> + <configuration> + <artifacts> + <artifact> + <file>${project.build.outputDirectory}/metadata.xml</file> + <type>xml</type> + </artifact> + </artifacts> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/maintenance/redhat-camel-spring-boot-patch-metadata/src/main/resources/metadata.xml b/maintenance/redhat-camel-spring-boot-patch-metadata/src/main/resources/metadata.xml new file mode 100644 index 00000000000..5f9756151d0 --- /dev/null +++ b/maintenance/redhat-camel-spring-boot-patch-metadata/src/main/resources/metadata.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + + Copyright 2005-2023 Red Hat, Inc. + + Red Hat 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. + +--> +<metadata xmlns="urn:redhat:patch-metadata:1"> + <product-bom groupId="com.redhat.camel.springboot.platform" artifactId="camel-spring-boot-bom" versions="[3.18,3.19)" /> + <cves> + <!--cve id="CVE-2023-123456" description="A CVE" + cve-link="https://cve.mitre.org/cgi-bin/cvename.cgi?name=2023-123456" + bz-link="https://bugzilla.redhat.com/show_bug.cgi?id=0123456789"> + <affects groupId="com.example" artifactId="example-*" versions="[1.0, 1.2)" fix="1.2" /> + </cve--> + </cves> + <fixes> + <!--fix id="HF0-1" description="Camel on Spring Boot 3.18 HF0 (example) - Camel Core fix"> + <affects groupId="org.apache.camel" artifactId="camel-core" versions="[3.0,${patch.camel-version})" fix="${patch.camel-version}" /> + </fix--> + </fixes> +</metadata> diff --git a/maintenance/redhat-camel-spring-boot-patch-repository/pom.xml b/maintenance/redhat-camel-spring-boot-patch-repository/pom.xml new file mode 100644 index 00000000000..9af02370c5a --- /dev/null +++ b/maintenance/redhat-camel-spring-boot-patch-repository/pom.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2005-2023 Red Hat, Inc. + + Red Hat 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. + +--> +<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"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>redhat-camel-spring-boot-maintenance</artifactId> + <version>4.0.0-SNAPSHOT</version> + </parent> + + <artifactId>redhat-camel-spring-boot-patch-repository</artifactId> + <packaging>pom</packaging> + <name>Camel SB Patch Repository</name> + + <description> + Offline repository with patched artifacts and patch metadata for Camel on Spring Boot applications + </description> + + <properties> + <patch.camel-version>4.0.0</patch.camel-version> + </properties> + + <dependencies> + <dependency> + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>redhat-camel-spring-boot-patch-metadata</artifactId> + <version>${project.version}</version> + <type>xml</type> + </dependency> + <!-- + Dependencies used to create a patch - these most probably use different version than the ones + from parent pom and the BOMs declared there. That's why they have to be specified at this level + --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-core</artifactId> + <version>${patch.camel-version}</version> + </dependency> +<!-- <dependency>--> +<!-- <groupId>io.netty</groupId>--> +<!-- <artifactId>netty-buffer</artifactId>--> +<!-- <version>${patch.netty-version}</version>--> +<!-- </dependency>--> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>unpack-dependencies</id> + <phase>prepare-package</phase> + <goals> + <goal>copy-dependencies</goal> + </goals> + <configuration> + <outputDirectory>${project.build.directory}/patch-repository</outputDirectory> + <useRepositoryLayout>true</useRepositoryLayout> + <copyPom>true</copyPom> + <addParentPoms>true</addParentPoms> + <excludeTransitive>false</excludeTransitive> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>create-repository</id> + <phase>prepare-package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <descriptors> + <descriptor>src/main/descriptors/repository.xml</descriptor> + </descriptors> + <finalName>${project.artifactId}-${project.version}</finalName> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/maintenance/redhat-camel-spring-boot-patch-repository/src/main/descriptors/repository.xml b/maintenance/redhat-camel-spring-boot-patch-repository/src/main/descriptors/repository.xml new file mode 100644 index 00000000000..fc304cfaa5d --- /dev/null +++ b/maintenance/redhat-camel-spring-boot-patch-repository/src/main/descriptors/repository.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2005-2020 Red Hat, Inc. + + Red Hat 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. + +--> +<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd"> + + <id>redhat-camel-spring-boot-patch-repository</id> + + <formats> + <format>zip</format> + </formats> + + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <!-- + There is only one file set for Camel on Spring Boot patches - a set that creates Maven repository structure. + We always have transitive dependencies in target/patch-repository, but this means we may get some Apache + or parent poms we don't need. + Remember - this file set impacts the content of actual patch and it's important to manage the content + properly. + --> + <fileSet> + <outputDirectory>repository</outputDirectory> + <directory>${project.build.directory}/patch-repository</directory> + <includes> + <include>com/redhat/camel/springboot/**</include> + </includes> + </fileSet> + </fileSets> + +</assembly> diff --git a/pom.xml b/pom.xml index f78f1173a91..c1486ac4f30 100644 --- a/pom.xml +++ b/pom.xml @@ -139,9 +139,12 @@ <springdoc-version>1.7.0</springdoc-version> <surefire.version>${maven-surefire-plugin-version}</surefire.version> <swagger-parser-v3-version>2.1.10</swagger-parser-v3-version> - <cyclonedx-maven-plugin-version>2.7.9</cyclonedx-maven-plugin-version> - </properties> + <cyclonedx-maven-plugin-version>2.7.9</cyclonedx-maven-plugin-version> + <!-- Added for productized build - plexus versions used by patch maven plugin --> + <openshift-maven-plugin-version>1.13.1.redhat-00001</openshift-maven-plugin-version> + <plexus-component-metadata-plugin-version>2.1.1</plexus-component-metadata-plugin-version> + </properties> <!-- Comment out the snapshot repositories as we don't need them now --> <repositories> @@ -425,6 +428,16 @@ </execution> </executions> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <version>3.5.0</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <version>3.5.0</version> + </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> @@ -503,6 +516,16 @@ </excludes> </configuration> </plugin> + <plugin> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-component-metadata</artifactId> + <version>${plexus-component-metadata-plugin-version}</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>${build-helper-maven-plugin-version}</version> + </plugin> </plugins> </pluginManagement> </build> @@ -933,5 +956,12 @@ </plugins> </build> </profile> + <profile> + <id>maintenance</id> + <modules> + <module>maintenance</module> + </modules> + </profile> + </profiles> </project> diff --git a/tooling/pom.xml b/tooling/pom.xml index 53be033b6e9..d3028f309cf 100644 --- a/tooling/pom.xml +++ b/tooling/pom.xml @@ -40,6 +40,9 @@ <module>camel-spring-boot-bom</module> <module>camel-starter-parent</module> <module>camel-spring-boot-starter-generator</module> + <module>redhat-camel-spring-boot-bom-generator</module> + <module>redhat-camel-spring-boot-bom</module> + <module>redhat-patch-maven-plugin</module> </modules> <build> diff --git a/tooling/redhat-camel-spring-boot-bom/pom.xml b/tooling/redhat-camel-spring-boot-bom/pom.xml new file mode 100644 index 00000000000..c6beb5c913e --- /dev/null +++ b/tooling/redhat-camel-spring-boot-bom/pom.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2005-2022 Red Hat, Inc. + + Red Hat 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. + +--> +<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/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>camel-spring-boot-bom</artifactId> + <version>4.0.0-M3-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>Red Hat Application Foundations :: Camel Spring Boot BOM</name> + <description>Red Hat Application Foundations provides organizations with a comprehensive set of components to develop and modernize their software</description> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + </properties> + + <url>https://developers.redhat.com/products/fuse/overview</url> + + <scm> + <connection>scm:git:[email protected]:jboss-fuse/redhat-fuse.git</connection> + <developerConnection>scm:git:[email protected]:jboss-fuse/redhat-fuse.git</developerConnection> + <tag>master</tag> + </scm> + + <organization> + <name>Red Hat</name> + <url>http://redhat.com</url> + </organization> + + <developers> + <developer> + <id>fuseteam</id> + <name>Red Hat Development Team</name> + <organization>Red Hat</organization> + <organizationUrl>http://www.redhat.com/</organizationUrl> + </developer> + </developers> + + <inceptionYear>2022</inceptionYear> + + <licenses> + <license> + <name>The Apache Software License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> + <distribution>repo</distribution> + </license> + </licenses> + + <!-- These are place holder dependency management entries --> + <dependencyManagement> + <dependencies> + <!-- Insert third party overrides here --> + <dependency> + <groupId>org.yaml</groupId> + <artifactId>snakeyaml</artifactId> + <version>2.0</version> + </dependency> + <dependency> + <groupId>org.hsqldb</groupId> + <artifactId>hsqldb</artifactId> + <version>2.7.1</version> + </dependency> + <!-- Overrides undertow-core/servlet since SB is using an older version --> + <dependency> + <groupId>io.undertow</groupId> + <artifactId>undertow-core</artifactId> + <version>2.3.5.Final</version> + </dependency> + <dependency> + <groupId>io.undertow</groupId> + <artifactId>undertow-servlet</artifactId> + <version>2.3.5.Final</version> + </dependency> + <!-- Overrides elastic-search dependency since SB is using an older version --> + <dependency> + <groupId>org.elasticsearch.client</groupId> + <artifactId>elasticsearch-rest-client</artifactId> + <version>8.6.2</version> + </dependency> + <dependency> + <groupId>org.elasticsearch.client</groupId> + <artifactId>elasticsearch-rest-client-sniffer</artifactId> + <version>8.6.2</version> + </dependency> + + <!-- Artemis Overrides --> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-commons</artifactId> + <version>2.26.0</version> + <exclusions> + <exclusion> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-core-client</artifactId> + <version>2.26.0</version> + <exclusions> + <exclusion> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-json_1.0_spec</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-jdbc-store</artifactId> + <version>2.26.0</version> + </dependency> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-jms-client</artifactId> + <version>2.26.0</version> + <exclusions> + <exclusion> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-json_1.0_spec</artifactId> + </exclusion> + </exclusions> + </dependency> + <!-- Spring Boot Dependencies --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-dependencies</artifactId> + <version>3.0.6</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <!-- Camel Spring Boot BOM --> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-spring-boot-bom</artifactId> + <version>4.0.0-M3-SNAPSHOT</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <!-- CXF BOM --> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-bom</artifactId> + <version>4.0.0</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> +</project> diff --git a/tooling/redhat-patch-maven-plugin/pom.xml b/tooling/redhat-patch-maven-plugin/pom.xml new file mode 100644 index 00000000000..7f1083b35f3 --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/pom.xml @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<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/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>tooling</artifactId> + <version>4.0.0-SNAPSHOT</version> + </parent> + + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>patch-maven-plugin</artifactId> + <packaging>jar</packaging> + <name>Camel SB Tooling :: Patch Maven Plugin</name> + + <description>Maven plugin that adjusts dependencies according to patch/security metadata.</description> + + <dependencies> + + <!-- Maven --> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-core</artifactId> + <version>${maven-version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.maven.plugin-tools</groupId> + <artifactId>maven-plugin-annotations</artifactId> + <version>${maven-plugin-plugin-version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-plugin-api</artifactId> + <version>${maven-version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-provider-api</artifactId> + <version>${maven-wagon-version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-embedder</artifactId> + <version>${maven-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-compat</artifactId> + <version>${maven-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-api</artifactId> + <version>${maven-resolver-version}</version> + </dependency> + <dependency> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-util</artifactId> + <version>${maven-resolver-version}</version> + </dependency> + <dependency> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-spi</artifactId> + <version>${maven-resolver-version}</version> + </dependency> + <dependency> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-impl</artifactId> + <version>${maven-resolver-version}</version> + </dependency> + <dependency> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-connector-basic</artifactId> + <version>${maven-resolver-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-transport-wagon</artifactId> + <version>${maven-resolver-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-file</artifactId> + <version>${maven-wagon-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-utils</artifactId> + <version>3.5.1</version> + </dependency> + + <!-- logging --> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + <version>${log4j2-version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>${commons-io-version}</version> + <scope>test</scope> + </dependency> + + <!-- testing --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>${junit-jupiter-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>${junit-jupiter-version}</version> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + </resource> + </resources> + <plugins> + <plugin> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-component-metadata</artifactId> + <executions> + <execution> + <goals> + <goal>generate-metadata</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>it-maven-projects</id> + <modules> + <module>src/it/cve-dependency-management</module> + </modules> + </profile> + </profiles> + +</project> diff --git a/tooling/redhat-patch-maven-plugin/src/it/cve-dependency-management/module1/pom.xml b/tooling/redhat-patch-maven-plugin/src/it/cve-dependency-management/module1/pom.xml new file mode 100644 index 00000000000..350c491901b --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/it/cve-dependency-management/module1/pom.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2005-2023 Red Hat, Inc. + + Licensed 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. + +--> +<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/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>dependency-management-example</artifactId> + <version>4.0.0</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>dependency-management-example-module1</artifactId> + + <dependencies> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-core</artifactId> + </dependency> + </dependencies> + +</project> diff --git a/tooling/redhat-patch-maven-plugin/src/it/cve-dependency-management/pom.xml b/tooling/redhat-patch-maven-plugin/src/it/cve-dependency-management/pom.xml new file mode 100644 index 00000000000..52710e22b12 --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/it/cve-dependency-management/pom.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2005-2023 Red Hat, Inc. + + Licensed 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. + +--> +<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/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>dependency-management-example</artifactId> + <version>4.0.0</version> + <packaging>pom</packaging> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + + <camel-spring-boot-bom-version>${project.version}</camel-spring-boot-bom-version> + </properties> + + <modules> + <module>module1</module> + </modules> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>camel-spring-boot-bom</artifactId> + <version>${camel-spring-boot-bom-version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <build> + <plugins> + <plugin> + <groupId>com.redhat.camel.springboot.platform</groupId> + <artifactId>patch-maven-plugin</artifactId> + <version>${camel-spring-boot-bom-version}</version> + <extensions>true</extensions> + <configuration> + <skip>false</skip> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/SecureDependencyManagement.java b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/SecureDependencyManagement.java new file mode 100644 index 00000000000..e1e2c5ba7b5 --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/SecureDependencyManagement.java @@ -0,0 +1,742 @@ +/* + * 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.camel.springboot.patch; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.UUID; + +import org.apache.camel.springboot.patch.extensions.ZipWagon; +import org.apache.camel.springboot.patch.model.AffectedArtifactSpec; +import org.apache.camel.springboot.patch.model.CVE; +import org.apache.camel.springboot.patch.model.Fix; +import org.apache.camel.springboot.patch.model.PatchMetadata; +import org.apache.camel.springboot.patch.model.ProductVersion; +import org.apache.maven.AbstractMavenLifecycleParticipant; +import org.apache.maven.MavenExecutionException; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; +import org.apache.maven.artifact.versioning.ComparableVersion; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.Profile; +import org.apache.maven.model.Repository; +import org.apache.maven.model.building.DefaultModelBuildingRequest; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.interpolation.StringVisitorModelInterpolator; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.Xpp3DomBuilder; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.metadata.DefaultMetadata; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.resolution.MetadataRequest; +import org.eclipse.aether.resolution.MetadataResult; +import org.slf4j.Logger; + +/** + * <p>The core mechanism that alters dependencyManagement and dependencies of the project where this plugin is + * declared.</p> + * + * <p>The point is to fetch relevant metadata from any of the configured Maven repositories and use it to alter + * the dependencies.</p> + * + * <p>This plugin was originally created for Fuse 7 and was supporting two different BOMs. Here we support only one.</p> + */ +@Component(role = AbstractMavenLifecycleParticipant.class) +public class SecureDependencyManagement extends AbstractMavenLifecycleParticipant { + + @Requirement + private Logger logger; + + @Requirement + private PlexusContainer container; + + @Requirement + RepositorySystem repo; + + // coordinates of the plugin itself + private String pluginGroupId; + private String pluginArtifactId; + + // coordinates of the patch metadata for Camel Spring Boot + private String mdCsbGroupId; + private String mdCsbArtifactId; + private String mdCsbType; + + // coordinates of the official Camel Spring Boot BOM + private String bomCsbGroupId; + private String bomCsbArtifactId; + + private File tmpDir = null; + + @Override + public void afterSessionStart(MavenSession session) throws MavenExecutionException { + super.afterSessionStart(session); + } + + @Override + public void afterSessionEnd(MavenSession session) throws MavenExecutionException { + super.afterSessionEnd(session); + if (tmpDir != null) { + cleanupRepository(tmpDir); + } + } + + @Override + public void afterProjectsRead(MavenSession session) throws MavenExecutionException { + if (session == null) { + return; + } + long ts = System.currentTimeMillis(); + configureProperties(session); + + if (shouldSkip(session)) { + return; + } + + logger.info("\n\n========== Red Hat Maven patching ==========\n"); + + try { + // detect which BOM we're using + // we have to review "original model", because at this stage, the effective <dependencyManagement> + // has already its bom-imports "resolved" + // (by org.apache.maven.model.building.DefaultModelBuilder.importDependencyManagement()) + Dependency bom = findProductBOM(session); + if (bom == null) { + return; + } + + List<RemoteRepository> repositories = configureRepositories(session); + + List<MetadataRequest> metadataRequests = configurePatchMetadataRequest(session, bom, repositories); + List<MetadataResult> metadataResults = repo.resolveMetadata(session.getRepositorySession(), metadataRequests); + + // The metadata result may come from different sources (latest /metadata/versioning/lastUpdated wins), but + // also may contain metadata versions for different CSB versions, like: + // <?xml version="1.0" encoding="UTF-8"?> + // <metadata> + // <groupId>com.redhat.camel.springboot.platform</groupId> + // <artifactId>redhat-camel-spring-boot-patch-metadata</artifactId> + // <versioning> + // <release>3.21.0.redhat-00001</release> + // <versions> + // <version>3.21.0.redhat-00001</version> + // <version>3.20.0.redhat-00005</version> + // <version>3.20.0.redhat-00001</version> + // </versions> + // <lastUpdated>20230101010101</lastUpdated> + // </versioning> + // </metadata> + // thus we have to find proper version of patch metadata depending on which version we're running in + // (in terms of used product BOM) + String version = findLatestMetadataVersion(bom, metadataResults); + if (version == null) { + logger.warn("[PATCH] Can't find latest patch metadata for {} in any of the configured repositories.", + String.format("%s/%s/%s BOM", bom.getGroupId(), bom.getArtifactId(), bom.getVersion())); + if (!logger.isDebugEnabled()) { + logger.warn("[PATCH] Please enable debug logging (-X) to see more details." + + " Perhaps the metadata was previously downloaded from different repository?"); + } + return; + } + + // we'll be looking for metadata specific to given current product + ArtifactRequest request = configurePatchArtifactRequest(session, bom, version); + request.setRepositories(repositories); + + ArtifactResult result; + try { + result = repo.resolveArtifact(session.getRepositorySession(), request); + logger.info("[PATCH] Resolved patch descriptor: {}", result.getArtifact().getFile()); + } catch (ArtifactResolutionException e) { + logger.warn("[PATCH] Unable to find patch metadata in any of the configured repositories"); + return; + } + + PatchMetadata patch = readPatchMetadata(result.getArtifact().getFile()); + ProductVersion bomVersion = new ProductVersion(bom.getVersion()); + // validate if the metadata is for our project - just sanity check + if (!bomVersion.canUse(patch.getProductVersionRange())) { + logger.warn("[PATCH] Patch metadata is applicable to product version {} and can't be used with {}.", + patch.getProductVersionRange(), + String.format("%s/%s/%s BOM", bom.getGroupId(), bom.getArtifactId(), bom.getVersion())); + return; + } + + logger.info("[PATCH] Patch metadata found for {}/{}/{}", + patch.getProductGroupId(), patch.getProductArtifactId(), patch.getProductVersionRange()); + int cveCount = patch.getCves().size(); + int fixCount = patch.getFixes().size(); + if (cveCount > 0) { + logger.info("[PATCH] - patch contains {} CVE {}", cveCount, cveCount > 1 ? "fixes" : "fix"); + } + if (fixCount > 0) { + logger.info("[PATCH] - patch contains {} patch {}", fixCount, fixCount > 1 ? "fixes" : "fix"); + } + + if (cveCount > 0) { + logger.info("[PATCH] Processing managed dependencies to apply CVE fixes..."); + + for (CVE cve : patch.getCves()) { + logger.info("[PATCH] - {}", cve); + for (AffectedArtifactSpec spec : cve.getAffected()) { + logger.info("[PATCH] Applying change {}", spec); + for (MavenProject project : session.getProjects()) { + logger.info("[PATCH] Project {}:{}", project.getGroupId(), project.getArtifactId()); + if (project.getDependencyManagement() != null) { + for (Dependency dependency : project.getDependencyManagement().getDependencies()) { + if (spec.matches(dependency)) { + logger.info("[PATCH] - managed dependency: {}/{}/{} -> {}", + dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), + spec.getFixVersion()); + project.getManagedVersionMap().get(dependency.getManagementKey()).setResolvedVersion(spec.getFixVersion().toString()); + project.getManagedVersionMap().get(dependency.getManagementKey()).setVersion(spec.getFixVersion().toString()); + dependency.setVersion(spec.getFixVersion().toString()); + } + } + } + for (Dependency dependency : project.getDependencies()) { + if (spec.matches(dependency)) { + logger.info("[PATCH] - dependency: {}/{}/{} -> {}", + dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), + spec.getFixVersion()); + dependency.setVersion(spec.getFixVersion().toString()); + } + } + } + } + } + } + if (fixCount > 0) { + logger.info("[PATCH] Processing managed dependencies to apply patch fixes..."); + + for (Fix fix : patch.getFixes()) { + logger.info("[PATCH] - {}", fix); + for (AffectedArtifactSpec spec : fix.getAffected()) { + logger.info("[PATCH] Applying change {}", spec); + for (MavenProject project : session.getProjects()) { + logger.info("[PATCH] Project {}:{}", project.getGroupId(), project.getArtifactId()); + if (project.getDependencyManagement() != null) { + for (Dependency dependency : project.getDependencyManagement().getDependencies()) { + if (spec.matches(dependency)) { + logger.info("[PATCH] - managed dependency: {}/{}/{} -> {}", + dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), + spec.getFixVersion()); + project.getManagedVersionMap().get(dependency.getManagementKey()).setResolvedVersion(spec.getFixVersion().toString()); + project.getManagedVersionMap().get(dependency.getManagementKey()).setVersion(spec.getFixVersion().toString()); + dependency.setVersion(spec.getFixVersion().toString()); + } + } + } + for (Dependency dependency : project.getDependencies()) { + if (spec.matches(dependency)) { + logger.info("[PATCH] - dependency: {}/{}/{} -> {}", + dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), + spec.getFixVersion()); + dependency.setVersion(spec.getFixVersion().toString()); + } + } + } + } + } + } + } finally { + logger.info("[PATCH] Done in " + (System.currentTimeMillis() - ts) + "ms\n\n=================================================\n"); + } + } + + private void configureProperties(MavenSession session) throws MavenExecutionException { + Properties props = new Properties(); + try (InputStream is = getClass().getResourceAsStream("/plugin.properties")) { + props.load(is); + } catch (IOException e) { + throw new MavenExecutionException("Can't load plugin.properties", + session.getCurrentProject().getFile()); + } + + pluginGroupId = props.getProperty("plugin.groupId"); + pluginArtifactId = props.getProperty("plugin.artifactId"); + + mdCsbGroupId = props.getProperty("patch-metadata.csb.groupId"); + mdCsbArtifactId = props.getProperty("patch-metadata.csb.artifactId"); + mdCsbType = props.getProperty("patch-metadata.csb.type"); + + bomCsbGroupId = props.getProperty("bom.csb.groupId"); + bomCsbArtifactId = props.getProperty("bom.csb.artifactId"); + } + + private boolean shouldSkip(MavenSession session) { + // <configuration>/<skip> + Boolean skip = null; + + for (MavenProject p : session.getProjects()) { + for (Plugin bp : p.getBuildPlugins()) { + if ((pluginGroupId + ":" + pluginArtifactId).equals(bp.getKey())) { + if (bp.getConfiguration() instanceof Xpp3Dom) { + XmlPlexusConfiguration config = new XmlPlexusConfiguration((Xpp3Dom) bp.getConfiguration()); + if (config.getChild("skip") != null) { + skip = "true".equalsIgnoreCase(config.getChild("skip").getValue()); + } + } + break; + } + } + } + + if (session.getUserProperties().containsKey("skipPatch")) { + if (Boolean.parseBoolean(session.getUserProperties().getProperty("skipPatch"))) { + skip = true; + } + } + + return skip != null && skip; + } + + /** + * <p>This method returns list of {@link RemoteRepository repositories} to be checked for patch metadata and + * artifacts.</p> + * + * <p>Patch metadata and artifacts will be resolved using normal Maven mechanisms. We however provide special + * kind of {@link RemoteRepository} that can be accessed through provided ZIP file (shipped as a + * <em>patch</em>).</p> + * + * @param session + * @return + */ + private List<RemoteRepository> configureRepositories(MavenSession session) throws MavenExecutionException { + List<RemoteRepository> repositories = new ArrayList<>(); + + RemoteRepository zipRepository = null; + + String patch = session.getUserProperties().getProperty("patch"); + if ("true".equals(patch) || (patch != null && "".equals(patch.trim()))) { + logger.warn("[PATCH] -Dpatch used, but patch location not specified. Are you sure correct -Dpatch=location is used?"); + } else { + if (patch != null) { + File pf = new File(patch); + if (!pf.isFile()) { + logger.warn("[PATCH] Patch repository {} is not accessible. Project repositories will be used", patch); + } else { + String canonicalPath = null; + try { + canonicalPath = pf.getCanonicalPath(); + } catch (IOException ignored) { + } + logger.info("[PATCH] Reading metadata and artifacts from {}", canonicalPath); + + // instead of "opening" and "closing" ZIP patch repository in ZipWagon, + // we'll unpack it now + tmpDir = ZipWagon.unpackPatchRepository(pf); + + // ID of the repository is tricky question. If we use pf.getName() as ID (which looks nice), we + // may have problems later, because that's what Aether does with its _remote.repositories file! + // for example if the patch file was patch-1.zip and we used it as repo id, we'd get + // _remote.repositories file with: + // camel-spring-boot-patch-metadata-<version>.xml>patch-1.zip= + // and next resolution of com.redhat.camel.springboot.platform:camel-spring-boot-patch-metadata:RELEASE with + // different patch of from remote repos would ONLY because the ID of the repo wouldn't match... + RemoteRepository.Builder zipRepositoryBuilder + = new RemoteRepository.Builder("csb-patch", "default", tmpDir.toURI().toString()); + zipRepository = zipRepositoryBuilder.build(); + repositories.add(zipRepository); + } + } + } + + if (repositories.size() == 0) { + for (org.apache.maven.model.Repository repo : session.getCurrentProject().getRepositories()) { + String id = repo.getId() == null ? UUID.randomUUID().toString() : repo.getId(); + RemoteRepository.Builder builder = new RemoteRepository.Builder(id, repo.getLayout(), repo.getUrl()); + repositories.add(builder.build()); + } + logger.info("[PATCH] Reading patch metadata and artifacts from {} project {}", repositories.size(), + repositories.size() > 1 ? "repositories" : "repository"); + for (RemoteRepository r : repositories) { + logger.info("[PATCH] - {}: {}", r.getId(), r.getUrl()); + } + } + + if (zipRepository != null) { + Repository r = new Repository(); + r.setId(zipRepository.getId()); + r.setLayout("default"); + try { + r.setUrl(tmpDir.toURI().toURL().toString()); + } catch (MalformedURLException ignored) { + } + for (MavenProject mp : session.getAllProjects()) { + mp.getRemoteProjectRepositories().add(zipRepository); + } + } + + return repositories; + } + + /** + * Iterate over all the projects in the session, check their {@code <dependencyManagement>} and + * checks a list of all {@link Dependency} for them. These will have all the placeholders + * resolved (because for example the version may have been parameterized) - this is required, + * because {@link MavenProject#getOriginalModel()} was checked. Then, among all the dependencies we find the + * <em>product BOM</em> to identify which product we're using. + * + * @param session + * @return + */ + private Dependency findProductBOM(MavenSession session) { + Set<Dependency> result = new LinkedHashSet<>(); + + for (MavenProject mp : session.getProjects()) { + MavenProject _mp = mp; + while (_mp != null) { + DependencyManagement dm = _mp.getOriginalModel().getDependencyManagement(); + if (dm != null) { + List<Dependency> projectDependencies = new LinkedList<>(); + for (Dependency d : dm.getDependencies()) { + if ("import".equals(d.getScope()) && "pom".equals(d.getType())) { + projectDependencies.add(d); + } + } + result.addAll(interpolate(session, _mp, projectDependencies)); + } + if (_mp.getOriginalModel().getProfiles() != null) { + Set<String> activeProfiles = new HashSet<>(); + if (_mp.getActiveProfiles() != null) { + for (Profile ap : _mp.getActiveProfiles()) { + activeProfiles.add(ap.getId()); + } + } + for (Profile profile : _mp.getOriginalModel().getProfiles()) { + if (activeProfiles.contains(profile.getId())) { + + DependencyManagement pdm = profile.getDependencyManagement(); + if (pdm != null) { + List<Dependency> projectDependencies = new LinkedList<>(); + for (Dependency d : pdm.getDependencies()) { + if ("import".equals(d.getScope()) && "pom".equals(d.getType())) { + projectDependencies.add(d); + } + } + result.addAll(interpolate(session, _mp, projectDependencies)); + } + } + } + } + _mp = _mp.getParent(); + } + } + + Dependency csbBom = null; + + for (Dependency d : result) { + if (bomCsbGroupId.equals(d.getGroupId()) && bomCsbArtifactId.equals(d.getArtifactId())) { + csbBom = d; + } + } + + if (csbBom == null) { + logger.info("[PATCH] No project in the reactor uses Camel on Spring Boot product BOM. Skipping patch processing."); + return null; + } + + return csbBom; + } + + /** + * Use Maven machinery to interpolate possible properties in ad-hoc model with BOM-dependencies. + * @param session + * @param mp + * @param projectDependencies + * @return + */ + private List<Dependency> interpolate(MavenSession session, MavenProject mp, List<Dependency> projectDependencies) { + // when operating on org.apache.maven.project.MavenProject.getOriginalModel(), we won't + // get our model interpolated, so we have to do it ourselves + Model m = new Model(); + DependencyManagement dm = new DependencyManagement(); + m.setDependencyManagement(dm); + dm.getDependencies().addAll(projectDependencies); + + // properties from project hierarchy, starting from top + Properties props = new Properties(); + Deque<MavenProject> projects = new LinkedList<>(); + MavenProject _mp = mp; + while (_mp != null) { + projects.push(_mp); + _mp = _mp.getParent(); + } + while (projects.size() > 0) { + Properties _props = projects.pop().getProperties(); + if (_props != null) { + props.putAll(_props); + } + } + m.setProperties(props); + + StringVisitorModelInterpolator interpolator = new StringVisitorModelInterpolator(); + try { + // maven 3.8.x + Class<?> processorClass = getClass().getClassLoader().loadClass("org.apache.maven.model.interpolation.DefaultModelVersionProcessor"); + Class<?> parameterClass = getClass().getClassLoader().loadClass("org.apache.maven.model.interpolation.ModelVersionProcessor"); + Method setVersionPropertiesProcessor = interpolator.getClass().getMethod("setVersionPropertiesProcessor", parameterClass); + setVersionPropertiesProcessor.invoke(interpolator, processorClass.getConstructor().newInstance()); + } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ignored) { + } + ModelBuildingRequest req = new DefaultModelBuildingRequest(); + req.getSystemProperties().putAll(session.getSystemProperties()); + req.getUserProperties().putAll(session.getUserProperties()); + interpolator.interpolateModel(m, null, req, null); + + return m.getDependencyManagement().getDependencies(); + } + + /** + * Prepares {@link MetadataRequest}s to get maven-metadata.xml for proper groupId:artifactId for given product. We + * have to consult this metadata ourselves, because single groupId:artifactId is used for all the versions of + * supported products. + * @param session + * @param repositories + * @return + */ + private List<MetadataRequest> configurePatchMetadataRequest(MavenSession session, Dependency productBom, List<RemoteRepository> repositories) { + List<MetadataRequest> requests = new ArrayList<>(repositories.size()); + String groupId; + String artifactId; + if (bomCsbArtifactId.equals(productBom.getArtifactId())) { + groupId = mdCsbGroupId; + artifactId = mdCsbArtifactId; + + DefaultMetadata md = new DefaultMetadata(groupId, artifactId, "maven-metadata.xml", Metadata.Nature.RELEASE); + // local repository + requests.add(new MetadataRequest(md, null, "")); + // remote repositories + for (RemoteRepository repo : repositories) { + requests.add(new MetadataRequest(md, repo, "")); + } + } + + return requests; + } + + /** + * Finds the latest suitable version of the patch metadata to use + * @param bom + * @param results + * @return + */ + private String findLatestMetadataVersion(Dependency bom, List<MetadataResult> results) { + ProductVersion bomVersion = new ProductVersion(bom.getVersion()); + + Map<String, Versioning> metadata = new TreeMap<>(); + for (MetadataResult result : results) { + if (result != null && result.isResolved()) { + try (FileReader reader = new FileReader(result.getMetadata().getFile())) { + org.apache.maven.artifact.repository.metadata.Metadata md = new MetadataXpp3Reader().read(reader); + Versioning v = md.getVersioning(); + if (v != null) { + // we don't care about /metadata/versioning/release, because it may be for newly deployed + // metadata for older version + metadata.put(v.getLastUpdated(), v); + } + } catch (IOException | XmlPullParserException e) { + logger.warn("[PATCH] Problem parsing Maven Metadata {}: {}", result.getMetadata().getFile(), e.getMessage(), e); + } + } + } + + Set<ComparableVersion> versions = new TreeSet<>(Comparator.reverseOrder()); + // iterate from oldest to newest metadata, where newer overwrite older versions + for (Versioning versioning : metadata.values()) { + for (String version : versioning.getVersions()) { + // the problem is that "canonical" maven versions are ONLY: + // - major, major.minor or major.minor.build + // - major-qualifier, major.minor-qualifier or major.minor.build-qualifier + // when qualifier is parsable as int, it'll become "build number", when version is unparsable, + // everything becomes just the "qualifier", so: + // 1-1 == 1.0.0 with build number = 1 + // 1-a == 1.0.0 with qualifier = "a" + // 1.1.1.1 == 0.0.0 with qualifier = "1.1.1.1" + // so for example new org.apache.maven.artifact.versioning.DefaultArtifactVersion("1.2.3.4") will + // return: + // DefaultArtifactVersion.getMajorVersion(): "0" + // DefaultArtifactVersion.getQualifier(): "1.2.3.4" + // + // we can imagine the problems with jackson-databind 2.9.10.4-redhat-00001 or + // fuse-springboot-bom 7.7.0.fuse-sb2-770010-redhat-00001 (actual version in MRRC/ga) + // + // fortunately GenericArtifactVersions uses org.apache.maven.artifact.versioning.ComparableVersion + // when comparing, but we have to take care of checking major.minor version + ProductVersion metadataVersion = new ProductVersion(version); + if (bomCsbArtifactId.equals(bom.getArtifactId())) { + if (bomVersion.getMajor() != metadataVersion.getMajor() + || bomVersion.getMinor() != metadataVersion.getMinor()) { + logger.debug("[PATCH] Skipping metadata {}", version); + continue; + } + } + logger.debug("[PATCH] Found metadata {}", version); + versions.add(new ComparableVersion(version)); + } + } + + // simply return newest version + return versions.size() == 0 ? null : versions.iterator().next().toString(); + } + + /** + * Checks which BOM do we use in one of reactors projects (if at all) and prepares an {@link ArtifactRequest} + * to fetch relevant, product-dependent metadata. + * @param session + * @param productBom the only valid product BOM + * @param version + * @return + */ + private ArtifactRequest configurePatchArtifactRequest(MavenSession session, Dependency productBom, String version) { + ArtifactRequest request = new ArtifactRequest(); + + if (bomCsbArtifactId.equals(productBom.getArtifactId())) { + request.setArtifact(new DefaultArtifact(String.format("%s:%s:%s:%s", mdCsbGroupId, mdCsbArtifactId, + mdCsbType, version))); + } + + return request; + } + + /** + * Parses Patch metadata XML into {@link PatchMetadata} + * @param patchMetadataFile + * @return + * @throws MavenExecutionException + */ + private PatchMetadata readPatchMetadata(File patchMetadataFile) throws MavenExecutionException { + PatchMetadata patch = new PatchMetadata(); + + try { + try (FileReader reader = new FileReader(patchMetadataFile)) { + Xpp3Dom dom = Xpp3DomBuilder.build(reader); + + Xpp3Dom productDom = dom.getChild("product-bom"); + if (productDom == null) { + throw new IllegalStateException("Can't find <product-bom> element in patch metadata"); + } + patch.setProductGroupId(productDom.getAttribute("groupId")); + patch.setProductArtifactId(productDom.getAttribute("artifactId")); + patch.setProductVersionRange(AffectedArtifactSpec.GVS.parseVersionRange(productDom.getAttribute("versions"))); + + Xpp3Dom cvesWrapper = dom.getChild("cves"); + if (cvesWrapper != null) { + for (Xpp3Dom cveDom : cvesWrapper.getChildren("cve")) { + CVE cve = new CVE(); + cve.setId(cveDom.getAttribute("id")); + cve.setDescription(cveDom.getAttribute("description")); + cve.setCveLink(cveDom.getAttribute("cve-link")); + cve.setBzLink(cveDom.getAttribute("bz-link")); + patch.getCves().add(cve); + for (Xpp3Dom affects : cveDom.getChildren("affects")) { + AffectedArtifactSpec spec = new AffectedArtifactSpec(); + spec.setGroupIdSpec(affects.getAttribute("groupId")); + spec.setArtifactIdSpec(affects.getAttribute("artifactId")); + spec.setVersionRange(AffectedArtifactSpec.GVS.parseVersionRange(affects.getAttribute("versions"))); + spec.setFixVersion(AffectedArtifactSpec.GVS.parseVersion(affects.getAttribute("fix"))); + cve.getAffected().add(spec); + } + } + } + Xpp3Dom fixesWrapper = dom.getChild("fixes"); + if (fixesWrapper != null) { + for (Xpp3Dom fixDom : fixesWrapper.getChildren("fix")) { + Fix fix = new Fix(); + fix.setId(fixDom.getAttribute("id")); + fix.setDescription(fixDom.getAttribute("description")); + fix.setLink(fixDom.getAttribute("link")); + patch.getFixes().add(fix); + for (Xpp3Dom affects : fixDom.getChildren("affects")) { + AffectedArtifactSpec spec = new AffectedArtifactSpec(); + spec.setGroupIdSpec(affects.getAttribute("groupId")); + spec.setArtifactIdSpec(affects.getAttribute("artifactId")); + spec.setVersionRange(AffectedArtifactSpec.GVS.parseVersionRange(affects.getAttribute("versions"))); + spec.setFixVersion(AffectedArtifactSpec.GVS.parseVersion(affects.getAttribute("fix"))); + fix.getAffected().add(spec); + } + } + } + } + } catch (Exception e) { + throw new MavenExecutionException(e.getMessage(), e); + } + + return patch; + } + + private void cleanupRepository(File tmpDir) { + try { + Files.walkFileTree(tmpDir.toPath(), new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + file.toFile().delete(); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + dir.toFile().delete(); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + logger.warn("Problem during temporary patch repository cleanup: {}", e.getMessage(), e); + } + } + +} diff --git a/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/extensions/ZipWagon.java b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/extensions/ZipWagon.java new file mode 100644 index 00000000000..b6c1c73dbab --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/extensions/ZipWagon.java @@ -0,0 +1,296 @@ +/* + * Copyright 2005-2023 Red Hat, Inc. + * + * Licensed 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.camel.springboot.patch.extensions; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Enumeration; +import java.util.TimeZone; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.apache.maven.MavenExecutionException; +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer; +import org.apache.maven.wagon.ConnectionException; +import org.apache.maven.wagon.InputData; +import org.apache.maven.wagon.OutputData; +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.StreamWagon; +import org.apache.maven.wagon.TransferFailedException; +import org.apache.maven.wagon.authentication.AuthenticationException; +import org.apache.maven.wagon.authorization.AuthorizationException; +import org.apache.maven.wagon.resource.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//@Component(hint = "zip", role = Wagon.class, instantiationStrategy = "per-lookup") +public class ZipWagon extends StreamWagon { + + public static final Logger LOG = LoggerFactory.getLogger(ZipWagon.class); + private static final DateFormat FMT = new SimpleDateFormat("yyyyMMddHHmmss"); + + File base = null; + + static { + TimeZone timezone = java.util.TimeZone.getTimeZone("UTC"); + FMT.setTimeZone(timezone); + } + + /** + * Unpacks a ZIP file into normal directory structure available later as normal (remote) repository. + * @param zipFile + * @return + * @throws MavenExecutionException + */ + public static File unpackPatchRepository(File zipFile) throws MavenExecutionException { + File tmpDir = null; + MessageDigest md5 = null; + try { + tmpDir = Files.createTempDirectory(zipFile.getName() + "-").toFile(); + md5 = MessageDigest.getInstance("MD5"); + } catch (Exception e) { + throw new MavenExecutionException("Can't create temporary directory to unzip " + zipFile, e); + } + + try { + ZipFile zf = new ZipFile(zipFile); + byte[] buf = new byte[16384]; + for (Enumeration<? extends ZipEntry> e = zf.entries(); e.hasMoreElements(); ) { + ZipEntry ze = e.nextElement(); + if (ze.isDirectory()) { + continue; + } + String name = trimName(ze.getName()); + File target = new File(tmpDir, name); + if ("maven-metadata-local.xml".equals(target.getName())) { + target = new File(target.getParentFile(), "maven-metadata.xml"); + name = name.replaceFirst("maven-metadata-local.xml$", "maven-metadata.xml"); + } + if ("maven-metadata-local.xml.md5".equals(target.getName())) { + target = new File(target.getParentFile(), "maven-metadata.xml.md5"); + name = name.replaceFirst("maven-metadata-local.xml.md5$", "maven-metadata.xml.md5"); + } + boolean checksumFile = name.endsWith(".md5"); + if (!checksumFile) { + md5.reset(); + } else { + if (target.exists()) { + // let's remove the generated one + target.delete(); + } + } + target.getParentFile().mkdirs(); + try (InputStream is = zf.getInputStream(ze)) { + try (OutputStream os = new FileOutputStream(target)) { + int read = -1; + while ((read = is.read(buf)) > 0) { + os.write(buf, 0, read); + if (!checksumFile) { + md5.update(buf, 0, read); + } + } + } + } + + // even ef there may be (later) a checksum file in the ZIP, we'll generate one just in case + File targetMd5 = new File(tmpDir, name + ".md5"); + if (!targetMd5.exists()) { + try (FileWriter fw = new FileWriter(targetMd5)) { + byte[] sum = md5.digest(); + for (byte b : sum) { + fw.write(String.format("%02x", b).toUpperCase()); + } + } + targetMd5.setLastModified(ze.getTime()); + } + + target.getParentFile().setLastModified(ze.getTime()); + target.setLastModified(ze.getTime()); + } + } catch (IOException e) { + throw new MavenExecutionException("Problem extracting data from " + zipFile, e); + } + + return tmpDir; + } + + public static String trimName(String name) { + if (name.startsWith("system/")) { + name = name.substring("system/".length()); + } else if (name.startsWith("repository/")) { + name = name.substring("repository/".length()); + } + return name; + } + + @Override + protected void openConnectionInternal() throws ConnectionException, AuthenticationException { + if (getRepository() == null) { + throw new ConnectionException("Unable to operate with a null repository."); + } + + base = null; + String baseDir = getRepository().getUrl(); + if (baseDir.startsWith("zip:")) { + baseDir = baseDir.substring(4); + } + if (baseDir.startsWith("file:")) { + try { + base = new File(new URL(baseDir).toURI()); + } catch (URISyntaxException | MalformedURLException e) { + throw new ConnectionException("Can't use " + baseDir + " repository: " + e.getMessage(), e); + } + } else { + // rather strange case + base = new File(baseDir); + } + + fireSessionDebug("Opening connection to " + getRepository().getId()); + } + + @Override + public void closeConnection() throws ConnectionException { + fireSessionDebug("Closing connection to " + getRepository().getId()); + } + + @Override + public void fillInputData(InputData inputData) throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException { + // similar to org.apache.maven.wagon.providers.file.FileWagon.fillInputData + + Resource resource = inputData.getResource(); + + String name = resource.getName(); + + File file = new File(base, name); + + if ("maven-metadata.xml".equals(file.getName())) { + // we may want to generate it if it doesn't exist + if (!file.exists()) { + generateMavenMetadata(name, file); + } + } + + if (!file.exists()) { + throw new ResourceDoesNotExistException("File: " + file + " does not exist"); + } + + try { + InputStream in = new BufferedInputStream(new FileInputStream(file)); + + inputData.setInputStream(in); + + resource.setContentLength(file.length()); + + resource.setLastModified(file.lastModified()); + } catch (FileNotFoundException e) { + throw new TransferFailedException("Could not read from file: " + file.getAbsolutePath(), e); + } + } + + /** + * Converts layout-specific name into groupId + artifactId. + * @param name + * @return + */ + private String[] groupAndArtifactIds(String name) { + String[] split = name.split("/"); + String[] result = new String[2]; + result[0] = Arrays.stream(split, 0, split.length - 2).collect(Collectors.joining(".")); + result[1] = split[split.length - 2]; + + return result; + } + + @Override + public void fillOutputData(OutputData outputData) throws TransferFailedException { + throw new TransferFailedException("PUT not supported for ZIP repository"); + } + + private void generateMavenMetadata(String name, File file) throws TransferFailedException { + Metadata md = new Metadata(); + String[] ga = groupAndArtifactIds(name); + md.setGroupId(ga[0]); + md.setArtifactId(ga[1]); + Versioning v = new Versioning(); + md.setVersioning(v); + File[] versions = file.getParentFile().listFiles(); + + String latestRelease = null; + long lastUpdated = -1L; + if (versions != null) { + for (File fv : versions) { + v.addVersion(fv.getName()); + long lu = fv.lastModified(); + if (lu > lastUpdated) { + lastUpdated = lu; + latestRelease = fv.getName(); + } + } + if (lastUpdated != -1L) { + v.setLatest(latestRelease); + v.setRelease(latestRelease); + v.setLastUpdated(FMT.format(new Date(lastUpdated))); + } + } + + try { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + new MetadataXpp3Writer().write(baos, md); + + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(baos.toByteArray()); + } + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(baos.toByteArray()); + File md5File = new File(file.getParentFile(), "maven-metadata.xml.md5"); + try (FileWriter fw = new FileWriter(md5File)) { + byte[] sum = md5.digest(); + for (byte b : sum) { + fw.write(String.format("%02x", b).toUpperCase()); + } + } + } catch (NoSuchAlgorithmException e) { + LOG.warn("Can't generate MD5 checksum for maven-metadata.xml: {}", e.getMessage()); + } + } + } catch (IOException e) { + throw new TransferFailedException("Problem generating metadata for ZIP patch repository", e); + } + } + +} diff --git a/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/AffectedArtifactSpec.java b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/AffectedArtifactSpec.java new file mode 100644 index 00000000000..b124a437189 --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/AffectedArtifactSpec.java @@ -0,0 +1,121 @@ +/* + * Copyright 2005-2020 Red Hat, Inc. + * + * Licensed 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.camel.springboot.patch.model; + +import java.util.regex.Pattern; + +import org.apache.maven.model.Dependency; +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.eclipse.aether.version.InvalidVersionSpecificationException; +import org.eclipse.aether.version.Version; +import org.eclipse.aether.version.VersionRange; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AffectedArtifactSpec { + + public static final Logger LOG = LoggerFactory.getLogger(AffectedArtifactSpec.class); + public static final GenericVersionScheme GVS = new GenericVersionScheme(); + + private String groupIdSpec; + private String artifactIdSpec; + // TOCHECK: for now we're sticking to version ranges and no direct version + private transient VersionRange versionRange; + private String versionRangeString; + private transient Version fixVersion; + private String fixVersionString; + + private transient Pattern groupIdSpecPattern; + private transient Pattern artifactIdSpecPattern; + + public boolean matches(Dependency dependency) { + String g = dependency.getGroupId(); + String a = dependency.getArtifactId(); + Version v = null; + try { + if (dependency.getVersion() != null) { + v = GVS.parseVersion(dependency.getVersion()); + } + } catch (InvalidVersionSpecificationException e) { + LOG.warn("[PATCH] Problem parsing managed dependency version: {}", e.getMessage(), e); + } + + if (!(groupIdSpecPattern == null ? g.equals(groupIdSpec) : groupIdSpecPattern.matcher(g).matches())) { + return false; + } + if (!(artifactIdSpecPattern == null ? a.equals(artifactIdSpec) : artifactIdSpecPattern.matcher(a).matches())) { + return false; + } + if (v != null && versionRange != null && !(versionRange.containsVersion(v))) { + return false; + } + return true; + } + + public String getGroupIdSpec() { + return groupIdSpec; + } + + public void setGroupIdSpec(String groupIdSpec) { + this.groupIdSpec = groupIdSpec; + if (groupIdSpec.contains("*")) { + this.groupIdSpecPattern = Pattern.compile(groupIdSpec.replaceAll("\\.", ".").replaceAll("\\*", ".*")); + } + } + + public String getArtifactIdSpec() { + return artifactIdSpec; + } + + public void setArtifactIdSpec(String artifactIdSpec) { + this.artifactIdSpec = artifactIdSpec; + if (artifactIdSpec.contains("*")) { + this.artifactIdSpecPattern = Pattern.compile(artifactIdSpec.replaceAll("\\.", ".").replaceAll("\\*", ".*")); + } + } + + public VersionRange getVersionRange() { + return versionRange; + } + + public String getVersionRangeString() { + return versionRangeString; + } + + public void setVersionRange(VersionRange versionRange) { + this.versionRange = versionRange; + this.versionRangeString = versionRange == null ? null : versionRange.toString(); + } + + public Version getFixVersion() { + return fixVersion; + } + + public String getFixVersionString() { + return fixVersionString; + } + + public void setFixVersion(Version fixVersion) { + this.fixVersion = fixVersion; + this.fixVersionString = fixVersion == null ? null : fixVersion.toString(); + } + + @Override + public String toString() { + return String.format("%s/%s/%s -> %s", groupIdSpec, artifactIdSpec, versionRange, fixVersion); + } + +} diff --git a/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/CVE.java b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/CVE.java new file mode 100644 index 00000000000..c57dbdd6a97 --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/CVE.java @@ -0,0 +1,81 @@ +/* + * Copyright 2005-2020 Red Hat, Inc. + * + * Licensed 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.camel.springboot.patch.model; + +import java.util.LinkedList; +import java.util.List; + +public class CVE { + + private String id; + private String description; + private String cveLink; + private String bzLink; + + private final List<AffectedArtifactSpec> affected = new LinkedList<>(); + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List<AffectedArtifactSpec> getAffected() { + return affected; + } + + public String getCveLink() { + return cveLink; + } + + public void setCveLink(String cveLink) { + this.cveLink = cveLink; + } + + public String getBzLink() { + return bzLink; + } + + public void setBzLink(String bzLink) { + this.bzLink = bzLink; + } + + @Override + public String toString() { + String cveLink = this.cveLink != null && !this.cveLink.trim().equals("") ? this.cveLink : null; + String bzLink = this.bzLink != null && !this.bzLink.trim().equals("") ? this.bzLink : null; + if (cveLink != null && bzLink != null) { + return id + ": " + description + " (" + cveLink + ", " + bzLink + ")"; + } else if (cveLink != null) { + return id + ": " + description + " (" + cveLink + ")"; + } else if (bzLink != null) { + return id + ": " + description + " (" + bzLink + ")"; + } else { + return id + ": " + description; + } + } + +} diff --git a/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/Fix.java b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/Fix.java new file mode 100644 index 00000000000..21b07d29b73 --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/Fix.java @@ -0,0 +1,67 @@ +/* + * Copyright 2005-2020 Red Hat, Inc. + * + * Licensed 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.camel.springboot.patch.model; + +import java.util.LinkedList; +import java.util.List; + +public class Fix { + + private String id; + private String description; + private String link; + + private final List<AffectedArtifactSpec> affected = new LinkedList<>(); + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List<AffectedArtifactSpec> getAffected() { + return affected; + } + + public String getLink() { + return link; + } + + public void setLink(String link) { + this.link = link; + } + + @Override + public String toString() { + String link = this.link != null && !this.link.trim().equals("") ? this.link : null; + if (link != null) { + return id + ": " + description + " (" + link + ")"; + } else { + return id + ": " + description; + } + } + +} diff --git a/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/PatchMetadata.java b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/PatchMetadata.java new file mode 100644 index 00000000000..6cf62ea787c --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/PatchMetadata.java @@ -0,0 +1,73 @@ +/* + * Copyright 2005-2020 Red Hat, Inc. + * + * Licensed 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.camel.springboot.patch.model; + +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.aether.version.VersionRange; + +public class PatchMetadata { + + private long timestamp; + private String productGroupId; + private String productArtifactId; + private VersionRange productVersionRange; + + private final List<CVE> cves = new LinkedList<>(); + private final List<Fix> fixes = new LinkedList<>(); + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public void setProductGroupId(String productGroupId) { + this.productGroupId = productGroupId; + } + + public void setProductArtifactId(String productArtifactId) { + this.productArtifactId = productArtifactId; + } + + public void setProductVersionRange(VersionRange productVersionRange) { + this.productVersionRange = productVersionRange; + } + + public long getTimestamp() { + return timestamp; + } + + public String getProductGroupId() { + return productGroupId; + } + + public String getProductArtifactId() { + return productArtifactId; + } + + public VersionRange getProductVersionRange() { + return productVersionRange; + } + + public List<CVE> getCves() { + return cves; + } + + public List<Fix> getFixes() { + return fixes; + } + +} diff --git a/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/ProductVersion.java b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/ProductVersion.java new file mode 100644 index 00000000000..b569fbc608b --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/main/java/org/apache/camel/springboot/patch/model/ProductVersion.java @@ -0,0 +1,102 @@ +/* + * Copyright 2005-2020 Red Hat, Inc. + * + * Licensed 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.camel.springboot.patch.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.aether.version.InvalidVersionSpecificationException; +import org.eclipse.aether.version.VersionRange; + +import static org.apache.camel.springboot.patch.model.AffectedArtifactSpec.GVS; + +public class ProductVersion { + + private final int major; + private int minor = 0; + private String qualifier; + + public ProductVersion(String version) { +// DefaultArtifactVersion v = new DefaultArtifactVersion(version); + // 7.8.0 will be parsed correctly + // 7.8.0-fuse-xxx will be parsed correctly + // 7.8.0.1 won't be parsed correctly + // 7.8.0.redhat-xxx will give correct major.minor, but qualifier will be "redhat" instead of "redhat-xxx" + // 7.8.0-redhat-xxx will be parsed correctly + // + // but because we can't reliably get full qualifier, we'll parse the vesion manually + + StringTokenizer st = new StringTokenizer(version, "."); + List<Integer> digits = new ArrayList<>(); + StringBuilder qualifier = new StringBuilder(); + boolean inQualifier = false; + while (st.hasMoreTokens()) { + String part = st.nextToken(); + if (!inQualifier) { + for (char c : part.toCharArray()) { + if (!Character.isDigit(c)) { + // this part and the rest will be the qualifier + inQualifier = true; + break; + } + } + } + if (inQualifier) { + qualifier.append('.').append(part); + } else { + digits.add(Integer.parseInt(part)); + } + } + + if (digits.size() == 0) { + // case of "1-redhat" version... + major = 0; + minor = 0; + } else { + major = digits.get(0); + if (digits.size() > 1) { + minor = digits.get(1); + } + } + this.qualifier = qualifier.toString(); + if (this.qualifier.length() > 0) { + // skip the '.' + this.qualifier = this.qualifier.substring(1); + } + } + + public int getMajor() { + return major; + } + + public int getMinor() { + return minor; + } + + public String getQualifier() { + return qualifier; + } + + public boolean canUse(VersionRange productVersionRange) { + try { + return productVersionRange.containsVersion(GVS.parseVersion(String.format("%d.%d", major, minor))); + } catch (InvalidVersionSpecificationException ignored) { + return false; + } + } + +} diff --git a/tooling/redhat-patch-maven-plugin/src/main/resources/patch-metadata.xsd b/tooling/redhat-patch-maven-plugin/src/main/resources/patch-metadata.xsd new file mode 100644 index 00000000000..9a24ff5b83a --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/main/resources/patch-metadata.xsd @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + + Copyright 2005-2020 Red Hat, Inc. + + Red Hat 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. + +--> +<xs:schema targetNamespace="urn:redhat:patch-metadata:1" version="1.0" + elementFormDefault="qualified" + xmlns:tns="urn:redhat:patch-metadata:1" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <xs:element name="metadata" type="tns:MetadataType" /> + + <xs:complexType name="MetadataType"> + <xs:sequence> + <xs:element name="product-bom" type="tns:ProductBomType" /> + <xs:element name="cves" type="tns:CvesType" /> + <xs:element name="fixes" type="tns:FixesType" /> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="ProductBomType"> + <xs:attribute name="groupId" type="xs:string" /> + <xs:attribute name="artifactId" type="xs:string" /> + <xs:attribute name="versions" type="xs:string" /> + </xs:complexType> + + <xs:complexType name="CvesType"> + <xs:sequence> + <xs:element name="cve" type="tns:CveType" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="CveType"> + <xs:sequence> + <xs:element name="affects" type="tns:AffectsType" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute name="id" type="xs:string" use="required" /> + <xs:attribute name="description" type="xs:string" use="required" /> + <xs:attribute name="cve-link" type="xs:string" /> + <xs:attribute name="bz-link" type="xs:string" /> + </xs:complexType> + + <xs:complexType name="AffectsType"> + <xs:attribute name="groupId" type="xs:string" use="required" /> + <xs:attribute name="artifactId" type="xs:string" use="required" /> + <xs:attribute name="versions" type="xs:string" /> + <xs:attribute name="fix" type="xs:string" /> + </xs:complexType> + + <xs:complexType name="FixesType"> + <xs:sequence> + <xs:element name="fix" type="tns:FixType" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="FixType"> + <xs:sequence> + <xs:element name="affects" type="tns:AffectsType" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + <xs:attribute name="id" type="xs:string" use="required" /> + <xs:attribute name="description" type="xs:string" use="required" /> + <xs:attribute name="link" type="xs:string" /> + </xs:complexType> + +</xs:schema> diff --git a/tooling/redhat-patch-maven-plugin/src/main/resources/plugin.properties b/tooling/redhat-patch-maven-plugin/src/main/resources/plugin.properties new file mode 100644 index 00000000000..0cf0d8fba29 --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/main/resources/plugin.properties @@ -0,0 +1,28 @@ +# +# Copyright 2005-2023 Red Hat, Inc. +# +# Licensed 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. +# + +# coordinates of the plugin itself +plugin.groupId = ${project.groupId} +plugin.artifactId = ${project.artifactId} + +# coordinates of the patch metadata for Camel on Spring Boot +patch-metadata.csb.groupId = com.redhat.camel.springboot.platform +patch-metadata.csb.artifactId = redhat-camel-spring-boot-patch-metadata +patch-metadata.csb.type = xml + +# coordinates of the official Camel on Spring Boot BOM +bom.csb.groupId = com.redhat.camel.springboot.platform +bom.csb.artifactId = camel-spring-boot-bom diff --git a/tooling/redhat-patch-maven-plugin/src/test/java/org/apache/camel/springboot/patch/MavenTest.java b/tooling/redhat-patch-maven-plugin/src/test/java/org/apache/camel/springboot/patch/MavenTest.java new file mode 100644 index 00000000000..bed846d39c4 --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/test/java/org/apache/camel/springboot/patch/MavenTest.java @@ -0,0 +1,266 @@ +/* + * Copyright 2005-2023 Red Hat, Inc. + * + * Licensed 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.camel.springboot.patch; + +import java.io.File; +import java.util.Properties; + +import com.google.inject.AbstractModule; +import org.apache.commons.io.FileUtils; +import org.apache.maven.Maven; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.ComparableVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.Model; +import org.apache.maven.model.building.DefaultModelBuildingRequest; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.interpolation.DefaultModelVersionProcessor; +import org.apache.maven.model.interpolation.StringVisitorModelInterpolator; +import org.apache.maven.repository.internal.MavenRepositorySystemUtils; +import org.apache.maven.repository.legacy.LegacyRepositorySystem; +import org.codehaus.plexus.ContainerConfiguration; +import org.codehaus.plexus.DefaultContainerConfiguration; +import org.codehaus.plexus.DefaultPlexusContainer; +import org.codehaus.plexus.PlexusConstants; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.codehaus.plexus.classworlds.realm.ClassRealm; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; +import org.eclipse.aether.impl.DefaultServiceLocator; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; +import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.transport.wagon.WagonProvider; +import org.eclipse.aether.transport.wagon.WagonTransporterFactory; +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.eclipse.aether.version.Version; +import org.eclipse.aether.version.VersionRange; +import org.apache.camel.springboot.patch.extensions.ZipWagon; +import org.apache.camel.springboot.patch.model.ProductVersion; +import org.junit.jupiter.api.Test; +import org.slf4j.ILoggerFactory; +import org.slf4j.LoggerFactory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MavenTest { + + @Test + public void mavenComponentsUsingPlexus() throws Exception { + // org.apache.maven.cli.MavenCli.doMain(org.apache.maven.cli.CliRequest) + + ClassWorld world = new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader()); + ClassRealm realm = world.getRealm("plexus.core"); + + realm.setParentClassLoader(MavenTest.class.getClassLoader()); + + ContainerConfiguration cc = new DefaultContainerConfiguration() + .setClassWorld(world) + .setRealm(realm) + .setClassPathScanning(PlexusConstants.SCANNING_INDEX) + .setAutoWiring(true) + .setJSR250Lifecycle(true) + .setName("maven"); + + PlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() { + @Override + protected void configure() { + bind(ILoggerFactory.class).toInstance(LoggerFactory.getILoggerFactory()); + } + }); + + Maven maven = container.lookup(Maven.class); + System.out.println(maven); + + DefaultRepositorySystemSessionFactory sf = container.lookup(DefaultRepositorySystemSessionFactory.class); + System.out.println(sf); + + // (file:/data/ggrzybek/.m2/repository/org/apache/maven/maven-compat/3.6.3/maven-compat-3.6.3.jar <no signer certificates>) + System.out.println(container.lookup(org.apache.maven.repository.RepositorySystem.class)); + // (file:/data/ggrzybek/.m2/repository/org/apache/maven/resolver/maven-resolver-impl/1.4.1/maven-resolver-impl-1.4.1.jar <no signer certificates>) + System.out.println(container.lookup(org.eclipse.aether.RepositorySystem.class)); + } + + @Test + public void mavenComponentsUsingLocator() throws Exception { + DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); + locator.setService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class); + locator.setService(org.apache.maven.repository.RepositorySystem.class, LegacyRepositorySystem.class); +// locator.setService(org.eclipse.aether.RepositorySystem.class, DefaultRepositorySystem.class); + + org.apache.maven.repository.RepositorySystem mavenSystem + = locator.getService(org.apache.maven.repository.RepositorySystem.class); + System.out.println(mavenSystem); + // this can't be used this way, because it's annotation driven and dependencies are not injected + // using org.eclipse.aether.spi.locator.Service.initService() +// mavenSystem.resolve(new ArtifactResolutionRequest()); + + org.eclipse.aether.RepositorySystem resolverSystem + = locator.getService(org.eclipse.aether.RepositorySystem.class); + System.out.println(resolverSystem); + + DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); + LocalRepository localRepository = new LocalRepository(new File(System.getProperty("user.home"), ".m2/repository")); + session.setLocalRepositoryManager(resolverSystem.newLocalRepositoryManager(session, localRepository)); + + ArtifactRequest request = new ArtifactRequest(); + request.setArtifact(new DefaultArtifact("commons-io:commons-io:jar:2.8.0")); + ArtifactResult result = resolverSystem.resolveArtifact(session, request); + System.out.println(result.getArtifact().getFile()); + } + + @Test + public void embeddedModelInterpolation() { + StringVisitorModelInterpolator interpolator = new StringVisitorModelInterpolator(); + + Model model = new Model(); + + Dependency dependency = new Dependency(); + dependency.setGroupId("g"); + dependency.setArtifactId("${my.artifactId}"); + dependency.setVersion("${my.version}"); + dependency.setScope("import"); + dependency.setType("pom"); + model.setDependencyManagement(new DependencyManagement()); + model.getDependencyManagement().addDependency(dependency); + + Properties properties = new Properties(); + properties.setProperty("my.version", "1.0"); + model.setProperties(properties); + + System.setProperty("my.artifactId", "my-bom"); + ModelBuildingRequest req = new DefaultModelBuildingRequest(); + req.setSystemProperties(System.getProperties()); + interpolator.setVersionPropertiesProcessor(new DefaultModelVersionProcessor()); + interpolator.interpolateModel(model, null, req, null); + + assertEquals("1.0", model.getDependencyManagement().getDependencies().get(0).getVersion()); + assertEquals("my-bom", model.getDependencyManagement().getDependencies().get(0).getArtifactId()); + } + + @Test + public void versionParsing() throws Exception { + String[] versions = new String[] { + "1", "1.2", "1.2.3", "1.2.3.4", "1.2.3.4.5", + "1-1", "1.2-1", "1.2.3-1", "1.2.3.4-1", "1.2.3.4.5-1", + "1-a", "1.2-a", "1.2.3-a", "1.2.3.4-a", "1.2.3.4.5-a", + "1.redhat", "1.2.redhat", "1.2.3.redhat", "1.2.3.4.redhat", "1.2.3.4.5.redhat", + "1.redhat-1", "1.2.redhat-1", "1.2.3.redhat-1", "1.2.3.4.redhat-1", "1.2.3.4.5.redhat-1", + "1-redhat", "1.2-redhat", "1.2.3-redhat", "1.2.3.4-redhat", "1.2.3.4.5-redhat", + "1-redhat-1", "1.2-redhat-1", "1.2.3-redhat-1", "1.2.3.4-redhat-1", "1.2.3.4.5-redhat-1", + "1.csb-3_20_0-redhat-00001", "1.2.csb-3_20_0-redhat-00001", "1.2.3.csb-3_20_0-redhat-00001", + "1.2.3.4.csb-3_20_0-redhat-00001", "1.2.3.4.5.csb-3_20_0-redhat-00001", + "1-csb-3_20_0-redhat-00001", "1.2-csb-3_20_0-redhat-00001", "1.2.3-csb-3_20_0-redhat-00001", + "1.2.3.4-csb-3_20_0-redhat-00001", "1.2.3.4.5-csb-3_20_0-redhat-00001", + }; + System.out.println("Maven:"); + for (String v : versions) { + ArtifactVersion version = new DefaultArtifactVersion(v); + if (1 == version.getMajorVersion()) { + System.out.printf(" - %s -> [%s].[%s].[%s] number: [%s] qualifier: [%s]%n", v, + version.getMajorVersion(), version.getMinorVersion(), + version.getIncrementalVersion(), version.getBuildNumber(), + version.getQualifier()); + } else { + System.out.printf(" ! %s -> [%s].[%s].[%s] number: [%s] qualifier: [%s]%n", v, + version.getMajorVersion(), version.getMinorVersion(), + version.getIncrementalVersion(), version.getBuildNumber(), + version.getQualifier()); + } + } + + System.out.println("Aether:"); + GenericVersionScheme gvs = new GenericVersionScheme(); + for (String v : versions) { + Version version = gvs.parseVersion(v); + System.out.printf(" - %s -> %s%n", v, version); + } + + System.out.println("Comparable:"); + for (String v : versions) { + ComparableVersion version = new ComparableVersion(v); + System.out.printf(" - %s -> %s%n", v, version.getCanonical()); + } + } + + @Test + public void comparingVersions() throws Exception { + GenericVersionScheme gvs = new GenericVersionScheme(); + //assertTrue(gvs.parseVersion("2.9.10.4-redhat-00001").compareTo(gvs.parseVersion("10.0.0.1-redhat-00001")) < 0); + assertTrue(new DefaultArtifactVersion("2.9.10.4-redhat-00001").compareTo(new DefaultArtifactVersion("10.0.0.1-redhat-00001")) < 0); + } + + @Test + public void productVersions() { + assertEquals(1, new ProductVersion("1").getMajor()); + assertEquals(1, new ProductVersion("1.1").getMajor()); + assertEquals(1, new ProductVersion("1.1.1").getMajor()); + assertEquals(1, new ProductVersion("1.1.1.1").getMajor()); + assertEquals(1, new ProductVersion("1.1.1.1.1").getMajor()); + assertEquals(1, new ProductVersion("1.redhat").getMajor()); + assertEquals(1, new ProductVersion("1.1.redhat").getMajor()); + assertEquals(1, new ProductVersion("1.1.1.redhat").getMajor()); + assertEquals(1, new ProductVersion("1.1.1.1.redhat").getMajor()); + assertEquals(1, new ProductVersion("1.1.1.1.1.redhat").getMajor()); + assertEquals(0, new ProductVersion("1-redhat").getMajor()); + assertEquals(1, new ProductVersion("1.1-redhat").getMajor()); + assertEquals(1, new ProductVersion("1.1.1-redhat").getMajor()); + assertEquals(1, new ProductVersion("1.1.1.1-redhat").getMajor()); + assertEquals(1, new ProductVersion("1.1.1.1.1-redhat").getMajor()); + assertEquals("", new ProductVersion("1").getQualifier()); + assertEquals("", new ProductVersion("1.1").getQualifier()); + assertEquals("", new ProductVersion("1.1.1").getQualifier()); + assertEquals("", new ProductVersion("1.1.1.1").getQualifier()); + assertEquals("", new ProductVersion("1.1.1.1.1").getQualifier()); + assertEquals("redhat", new ProductVersion("1.redhat").getQualifier()); + assertEquals("redhat", new ProductVersion("1.1.redhat").getQualifier()); + assertEquals("redhat", new ProductVersion("1.1.1.redhat").getQualifier()); + assertEquals("redhat", new ProductVersion("1.1.1.1.redhat").getQualifier()); + assertEquals("redhat", new ProductVersion("1.1.1.1.1.redhat").getQualifier()); + assertEquals("1-redhat", new ProductVersion("1-redhat").getQualifier()); + assertEquals("1-redhat", new ProductVersion("1.1-redhat").getQualifier()); + assertEquals("1-redhat", new ProductVersion("1.1.1-redhat").getQualifier()); + assertEquals("1-redhat", new ProductVersion("1.1.1.1-redhat").getQualifier()); + assertEquals("1-redhat", new ProductVersion("1.1.1.1.1-redhat").getQualifier()); + } + + @Test + public void matchingVersions() throws Exception { + GenericVersionScheme gvs = new GenericVersionScheme(); + VersionRange range = gvs.parseVersionRange("[3.20, 3.21.1)"); + assertTrue(range.containsVersion(gvs.parseVersion("3.20.0"))); + assertTrue(range.containsVersion(gvs.parseVersion("3.20.0.redhat-00001"))); + assertTrue(range.containsVersion(gvs.parseVersion("3.20.1.redhat-00001"))); + assertTrue(range.containsVersion(gvs.parseVersion("3.21"))); + assertTrue(range.containsVersion(gvs.parseVersion("3.21.0"))); + assertTrue(range.containsVersion(gvs.parseVersion("3.21.0.1"))); + assertFalse(range.containsVersion(gvs.parseVersion("3.21.1"))); + assertTrue(range.containsVersion(gvs.parseVersion("3.21.redhat-00001"))); + assertTrue(range.containsVersion(gvs.parseVersion("3.21.0.redhat-00001"))); + assertFalse(range.containsVersion(gvs.parseVersion("3.21.1.redhat-00001"))); + } + +} diff --git a/tooling/redhat-patch-maven-plugin/src/test/java/org/apache/camel/springboot/patch/ZipWagonProvider.java b/tooling/redhat-patch-maven-plugin/src/test/java/org/apache/camel/springboot/patch/ZipWagonProvider.java new file mode 100644 index 00000000000..8351e632090 --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/test/java/org/apache/camel/springboot/patch/ZipWagonProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright 2005-2023 Red Hat, Inc. + * + * Licensed 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.camel.springboot.patch; + +import org.apache.maven.wagon.Wagon; +import org.eclipse.aether.transport.wagon.WagonProvider; +import org.apache.camel.springboot.patch.extensions.ZipWagon; + +//@Component(role = WagonProvider.class) +public class ZipWagonProvider implements WagonProvider { + + @Override + public Wagon lookup(String roleHint) throws Exception { + if (roleHint.equals("zip")) { + return new ZipWagon(); + } + return null; + } + + @Override + public void release(Wagon wagon) { + } + +} diff --git a/tooling/redhat-patch-maven-plugin/src/test/resources/log4j2.properties b/tooling/redhat-patch-maven-plugin/src/test/resources/log4j2.properties new file mode 100644 index 00000000000..0f8703c715c --- /dev/null +++ b/tooling/redhat-patch-maven-plugin/src/test/resources/log4j2.properties @@ -0,0 +1,28 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +appender.file.type = File +appender.file.name = file +appender.file.fileName = target/patch-maven-plugin.log +appender.file.layout.type = PatternLayout +appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +appender.out.type = Console +appender.out.name = out +appender.out.layout.type = PatternLayout +appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +rootLogger.level = INFO +rootLogger.appenderRef.file.ref = file diff --git a/tooling/redhat-patch-maven-plugin/src/test/resources/patch-1.zip b/tooling/redhat-patch-maven-plugin/src/test/resources/patch-1.zip new file mode 100644 index 00000000000..45c537feb59 Binary files /dev/null and b/tooling/redhat-patch-maven-plugin/src/test/resources/patch-1.zip differ
