diff --git a/eng/bomgenerator/buildAndRun.cmd b/eng/bomgenerator/buildAndRun.cmd
new file mode 100644
index 0000000000000..fd26bd8a1b556
--- /dev/null
+++ b/eng/bomgenerator/buildAndRun.cmd
@@ -0,0 +1,4 @@
+set "versioningClientFileLocation=%~dp0..\versioning\version_client.txt"
+set "bomPomFileLocation=%~dp0..\..\sdk\boms\azure-sdk-bom\pom.xml"
+set "outputFileLocation=%~dp0..\..\sdk\boms\azure-sdk-bom\newpom.xml"
+mvn clean install && mvn exec:java -Dexec.args="-inputFile=%versioningClientFileLocation% -outputFile=%outputFileLocation% -pomFile=%bomPomFileLocation%"
\ No newline at end of file
diff --git a/eng/bomgenerator/generatedpom.xml b/eng/bomgenerator/generatedpom.xml
new file mode 100644
index 0000000000000..100a97c076f87
--- /dev/null
+++ b/eng/bomgenerator/generatedpom.xml
@@ -0,0 +1,272 @@
+
+
+ 4.0.0
+ com.azure
+ azure-sdk-bom
+ 1.0.4-beta.1
+ pom
+ Azure Java SDK BOM (Bill of Materials)
+ Azure Java SDK BOM (Bill of Materials)
+ https://github.com/azure/azure-sdk-for-java
+
+
+ The MIT License (MIT)
+ http://opensource.org/licenses/MIT
+ repo
+
+
+
+
+ microsoft
+ Microsoft Corporation
+
+
+
+ scm:git:git://github.com/azure/azure-sdk-for-java
+ scm:git:git://github.com/azure/azure-sdk-for-java
+ https://github.com/azure/azure-sdk-for-java
+
+
+ GitHub
+ https://github.com/azure/azure-sdk-for-java/issues
+
+
+ UTF-8
+ ${project.build.directory}
+
+
+
+
+ com.azure
+ azure-ai-formrecognizer
+ 3.1.2
+
+
+ com.azure
+ azure-ai-metricsadvisor
+ 1.0.0
+
+
+ com.azure
+ azure-ai-textanalytics
+ 5.1.0
+
+
+ com.azure
+ azure-core
+ 1.18.0
+
+
+ com.azure
+ azure-core-amqp
+ 2.3.0
+
+
+ com.azure
+ azure-core-http-netty
+ 1.10.1
+
+
+ com.azure
+ azure-core-http-okhttp
+ 1.7.1
+
+
+ com.azure
+ azure-core-management
+ 1.3.1
+
+
+ com.azure
+ azure-core-serializer-json-gson
+ 1.1.4
+
+
+ com.azure
+ azure-core-serializer-json-jackson
+ 1.2.5
+
+
+ com.azure
+ azure-data-appconfiguration
+ 1.2.0
+
+
+ com.azure
+ azure-data-tables
+ 12.1.0
+
+
+ com.azure
+ azure-identity
+ 1.3.3
+
+
+ com.azure
+ azure-messaging-eventhubs
+ 5.9.0
+
+
+ com.azure
+ azure-messaging-servicebus
+ 7.3.0
+
+
+ com.azure
+ azure-security-keyvault-administration
+ 4.0.1
+
+
+ com.azure
+ azure-security-keyvault-certificates
+ 4.2.1
+
+
+ com.azure
+ azure-security-keyvault-jca
+ 1.0.1
+
+
+ com.azure
+ azure-security-keyvault-keys
+ 4.3.1
+
+
+ com.azure
+ azure-security-keyvault-secrets
+ 4.3.1
+
+
+ com.fasterxml.jackson
+ jackson-bom
+ 2.12.3
+ pom
+ import
+
+
+ com.google.code.gson
+ gson
+ 2.8.6
+
+
+ com.microsoft.azure
+ msal4j
+ 1.10.1
+
+
+ com.microsoft.azure
+ msal4j-persistence-extension
+ 1.1.0
+
+
+ com.microsoft.azure
+ qpid-proton-j-extensions
+ 1.2.4
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.8.1
+
+
+ io.netty
+ netty-bom
+ 4.1.65.Final
+ pom
+ import
+
+
+ io.netty
+ netty-tcnative-boringssl-static
+ 2.0.39.Final
+
+
+ io.projectreactor
+ reactor-bom
+ 2020.0.7
+ pom
+ import
+
+
+ net.java.dev.jna
+ jna-platform
+ 5.6.0
+
+
+ net.minidev
+ json-smart
+ 2.4.7
+
+
+ org.apache.qpid
+ proton-j
+ 0.33.8
+
+
+ org.linguafranca.pwdb
+ KeePassJava2
+ 2.1.4
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.30
+
+
+
+
+
+
+ maven-antrun-plugin
+ 1.8
+
+
+ copy
+ package
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+ dependency-checker
+
+
+ dependency-checker
+
+
+
+
+
+ net.jonathangiles.tools
+ dependencyChecker-maven-plugin
+ 1.0.6
+
+
+ package
+
+ check
+
+
+ html
+ true
+ true
+
+
+
+
+
+
+
+
+
diff --git a/eng/bomgenerator/pom.xml b/eng/bomgenerator/pom.xml
new file mode 100644
index 0000000000000..c91ee5b68ad28
--- /dev/null
+++ b/eng/bomgenerator/pom.xml
@@ -0,0 +1,63 @@
+
+
+ 4.0.0
+
+ com.azure.tools
+ bomGenerator
+ 1.0.0-beta.1
+
+
+ 11
+ 11
+
+
+
+
+ org.jboss.shrinkwrap.resolver
+ shrinkwrap-resolver-bom
+ 3.1.4
+ pom
+ import
+
+
+
+
+
+
+ org.jboss.shrinkwrap.resolver
+ shrinkwrap-resolver-impl-maven
+
+
+ org.apache.maven
+ maven-core
+ 3.5.4
+
+
+ org.slf4j
+ slf4j-log4j12
+ 1.7.5
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.6.0
+
+
+
+ java
+
+
+
+
+ com.azure.tools.bomgenerator.Main
+ false
+
+
+
+
+
diff --git a/eng/bomgenerator/readme.md b/eng/bomgenerator/readme.md
new file mode 100644
index 0000000000000..2194e93f4e027
--- /dev/null
+++ b/eng/bomgenerator/readme.md
@@ -0,0 +1,4 @@
+This tool generate the azure-sdk-bom.
+
+To run the tool provide the following arguments
+-inputFile= -outputFile= -pomFile=
\ No newline at end of file
diff --git a/eng/bomgenerator/report.log b/eng/bomgenerator/report.log
new file mode 100644
index 0000000000000..8ba34008fb9fa
--- /dev/null
+++ b/eng/bomgenerator/report.log
@@ -0,0 +1,60 @@
+Dropped dependency com.azure:azure-storage-blob-cryptography:12.12.0.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-search-documents:11.4.0.
+ Includes dependency com.azure:azure-core-serializer-json-jackson:1.2.4. Expected dependency com.azure:azure-core-serializer-json-jackson:1.2.5
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-storage-file-datalake:12.6.0.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-communication-chat:1.0.0.
+ Includes dependency com.azure:azure-core:1.14.1. Expected dependency com.azure:azure-core:1.18.0
+ Includes dependency com.azure:azure-communication-common:1.0.0. Expected dependency com.azure:azure-communication-common:1.0.3
+Dropped dependency com.azure:azure-storage-internal-avro:12.0.5.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-storage-common:12.12.0.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-digitaltwins-core:1.1.1.
+ Includes dependency com.azure:azure-core-http-netty:1.9.2. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core-serializer-json-jackson:1.2.3. Expected dependency com.azure:azure-core-serializer-json-jackson:1.2.5
+ Includes dependency com.fasterxml.jackson.core:jackson-annotations:2.12.2. Expected dependency com.fasterxml.jackson.core:jackson-annotations:2.12.3
+ Includes dependency com.azure:azure-core:1.16.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-storage-queue:12.10.0.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-communication-sms:1.0.3.
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+ Includes dependency com.azure:azure-communication-common:1.0.2. Expected dependency com.azure:azure-communication-common:1.0.3
+Dropped dependency com.azure:azure-communication-common:1.0.3.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-storage-blob:12.12.0.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-storage-blob-batch:12.10.0.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-storage-file-share:12.10.0.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-communication-phonenumbers:1.0.3.
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+ Includes dependency com.azure:azure-communication-common:1.0.2. Expected dependency com.azure:azure-communication-common:1.0.3
+Dropped dependency com.azure:azure-mixedreality-remoterendering:1.0.0.
+ Includes dependency com.azure:azure-core-http-netty:1.9.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.14.1. Expected dependency com.azure:azure-core:1.18.0
+ Includes dependency com.azure:azure-core:1.13.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-messaging-eventhubs-checkpointstore-blob:1.8.1.
+ Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-mixedreality-authentication:1.0.0.
+ Includes dependency com.azure:azure-core-http-netty:1.9.0. Expected dependency com.azure:azure-core-http-netty:1.10.1
+ Includes dependency com.azure:azure-core:1.14.1. Expected dependency com.azure:azure-core:1.18.0
+Dropped dependency com.azure:azure-cosmos:4.17.0.
+ Includes dependency org.slf4j:slf4j-api:1.7.31. Expected dependency org.slf4j:slf4j-api:1.7.30
+Dropped dependency com.azure:azure-communication-identity:1.1.1.
+ Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0
+ Includes dependency com.azure:azure-communication-common:1.0.2. Expected dependency com.azure:azure-communication-common:1.0.3
diff --git a/eng/bomgenerator/samplereport.txt b/eng/bomgenerator/samplereport.txt
new file mode 100644
index 0000000000000..c675e336972c3
--- /dev/null
+++ b/eng/bomgenerator/samplereport.txt
@@ -0,0 +1,21 @@
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - We dropped the following dependencies from the input list.
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-chat:1.0.0, Reason com.azure:azure-core:1.14.1
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-common:1.0.3, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-identity:1.1.1, Reason com.azure:azure-core:1.17.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-phonenumbers:1.0.3, Reason com.azure:azure-core:1.17.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-sms:1.0.3, Reason com.azure:azure-core:1.17.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-cosmos:4.17.0, Reason org.slf4j:slf4j-api:1.7.31
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-digitaltwins-core:1.1.1, Reason com.fasterxml.jackson.core:jackson-annotations:2.12.2
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-messaging-eventgrid:4.4.0, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-messaging-eventhubs-checkpointstore-blob:1.8.1, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-mixedreality-authentication:1.0.0, Reason com.azure:azure-core-http-netty:1.9.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-mixedreality-remoterendering:1.0.0, Reason com.azure:azure-core-http-netty:1.9.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-search-documents:11.4.0, Reason com.azure:azure-core-serializer-json-jackson:1.2.4
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-blob-batch:12.10.0, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-blob-cryptography:12.12.0, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-blob:12.12.0, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-common:12.12.0, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-file-datalake:12.6.0, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-file-share:12.10.0, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-internal-avro:12.0.5, Reason com.azure:azure-core-http-netty:1.10.0
+[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-queue:12.10.0, Reason com.azure:azure-core-http-netty:1.10.0
\ No newline at end of file
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/BomGenerator.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/BomGenerator.java
new file mode 100644
index 0000000000000..3f76d24ab8f78
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/BomGenerator.java
@@ -0,0 +1,183 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator;
+
+import com.azure.tools.bomgenerator.models.BomDependency;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.DependencyManagement;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.stream.Collectors;
+
+import static com.azure.tools.bomgenerator.Utils.BASE_AZURE_GROUPID;
+import static com.azure.tools.bomgenerator.Utils.AZURE_PERF_LIBRARY_IDENTIFIER;
+import static com.azure.tools.bomgenerator.Utils.AZURE_TEST_LIBRARY_IDENTIFIER;
+import static com.azure.tools.bomgenerator.Utils.EXCLUSION_LIST;
+import static com.azure.tools.bomgenerator.Utils.POM_TYPE;
+import static com.azure.tools.bomgenerator.Utils.SDK_DEPENDENCY_PATTERN;
+
+public class BomGenerator {
+ private String outputFileName;
+ private String inputFileName;
+ private String pomFileName;
+
+ private static Logger logger = LoggerFactory.getLogger(BomGenerator.class);
+
+ BomGenerator(String inputFileName, String outputFileName, String pomFileName) {
+ this.inputFileName = inputFileName;
+ this.outputFileName = outputFileName;
+ this.pomFileName = pomFileName;
+ }
+
+ public void generate() {
+ List inputDependencies = scan();
+ List externalDependencies = resolveExternalDependencies();
+
+ // 1. Create the initial tree and reduce conflicts.
+ // 2. And pick only those dependencies. that were in the input set, since they become the new roots of n-ary tree.
+ DependencyAnalyzer analyzer = new DependencyAnalyzer(inputDependencies, externalDependencies);
+ analyzer.reduce();
+ Collection outputDependencies = analyzer.getBomEligibleDependencies();
+
+ // 2. Create the new tree for the BOM.
+ analyzer = new DependencyAnalyzer(outputDependencies, externalDependencies);
+ boolean validationFailed = analyzer.validate();
+ outputDependencies = analyzer.getBomEligibleDependencies();
+
+ // 4. Create the new BOM file.
+ if(!validationFailed) {
+ // Rewrite the existing BOM to have the dependencies in the order in which we insert them, making the diff PR easier to review.
+ rewriteExistingBomFile();
+ writeBom(outputDependencies);
+ }
+ else {
+ logger.trace("Validation for the BOM failed. Exiting...");
+ }
+ }
+
+ private List scan() {
+ List inputDependencies = new ArrayList<>();
+
+ try {
+ for (String line : Files.readAllLines(Paths.get(inputFileName))) {
+ BomDependency dependency = scanDependency(line);
+
+ if(dependency != null) {
+ inputDependencies.add(dependency);
+ }
+ }
+ } catch (IOException exception) {
+ logger.error("Input file parsing failed. Exception{}", exception.toString());
+ }
+
+ return inputDependencies;
+ }
+
+ private BomDependency scanDependency(String line) {
+ Matcher matcher = SDK_DEPENDENCY_PATTERN.matcher(line);
+ if (!matcher.matches()) {
+ return null;
+ }
+
+ if (matcher.groupCount() != 3) {
+ return null;
+ }
+
+ String artifactId = matcher.group(1);
+ String version = matcher.group(2);
+
+ if(version.contains("-")) {
+ // This is a non-GA library
+ return null;
+ }
+
+ if (EXCLUSION_LIST.contains(artifactId)
+ || artifactId.contains(AZURE_PERF_LIBRARY_IDENTIFIER)
+ || (artifactId.contains(AZURE_TEST_LIBRARY_IDENTIFIER))) {
+ logger.trace("Skipping dependency {}:{}", BASE_AZURE_GROUPID, artifactId);
+ return null;
+ }
+
+ return new BomDependency(BASE_AZURE_GROUPID, artifactId, version);
+ }
+
+ private Model readModel() {
+ MavenXpp3Reader reader = new MavenXpp3Reader();
+ try {
+ Model model = reader.read(new FileReader(this.pomFileName));
+ return model;
+ } catch (XmlPullParserException | IOException e) {
+ logger.error("BOM reading failed with: {}", e.toString());
+ }
+
+ return null;
+ }
+
+ private void writeModel(Model model) {
+ String pomFileName = this.pomFileName;
+ writeModel(pomFileName, model);
+ }
+
+ private void writeModel(String fileName, Model model) {
+ MavenXpp3Writer writer = new MavenXpp3Writer();
+ try {
+ writer.write(new FileWriter(fileName), model);
+ } catch (IOException exception) {
+ logger.error("BOM writing failed with: {}", exception.toString());
+ }
+ }
+
+ private List resolveExternalDependencies() {
+ List externalDependencies = new ArrayList<>();
+ List externalBomDependencies = getExternalDependencies();
+ externalDependencies.addAll(Utils.getExternalDependenciesContent(externalBomDependencies));
+ return externalDependencies;
+ }
+
+ private List getExternalDependencies() {
+ Model model = readModel();
+ DependencyManagement management = model.getDependencyManagement();
+ return management.getDependencies().stream().filter(dependency -> dependency.getType().equals(POM_TYPE)).collect(Collectors.toList());
+ }
+
+ private void rewriteExistingBomFile() {
+ Model model = readModel();
+ DependencyManagement management = model.getDependencyManagement();
+ List dependencies = management.getDependencies();
+ dependencies.sort(new DependencyComparator());
+ management.setDependencies(dependencies);
+ writeModel(model);
+ }
+
+ private void writeBom(Collection bomDependencies) {
+ Model model = readModel();
+ DependencyManagement management = model.getDependencyManagement();
+ List externalBomDependencies = management.getDependencies().stream().filter(dependency -> dependency.getType().equals(POM_TYPE)).collect(Collectors.toList());
+ List dependencies = bomDependencies.stream().map(bomDependency -> {
+ Dependency dependency = new Dependency();
+ dependency.setGroupId(bomDependency.getGroupId());
+ dependency.setArtifactId(bomDependency.getArtifactId());
+ dependency.setVersion(bomDependency.getVersion());
+ return dependency;
+ }).collect(Collectors.toList());
+ dependencies.addAll(externalBomDependencies);
+ dependencies.sort(new DependencyComparator());
+ management.setDependencies(dependencies);
+ writeModel(this.outputFileName, model);
+ }
+}
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyAnalyzer.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyAnalyzer.java
new file mode 100644
index 0000000000000..795017e94cf45
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyAnalyzer.java
@@ -0,0 +1,274 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator;
+
+import com.azure.tools.bomgenerator.models.BomDependency;
+import com.azure.tools.bomgenerator.models.BomDependencyErrorInfo;
+import com.azure.tools.bomgenerator.models.BomDependencyNoVersion;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.jboss.shrinkwrap.resolver.api.maven.MavenFormatStage;
+import org.jboss.shrinkwrap.resolver.api.maven.MavenResolvedArtifact;
+import org.jboss.shrinkwrap.resolver.api.maven.MavenResolverSystemBase;
+import org.jboss.shrinkwrap.resolver.api.maven.MavenStrategyStage;
+import org.jboss.shrinkwrap.resolver.api.maven.PomEquippedResolveStage;
+import org.jboss.shrinkwrap.resolver.api.maven.PomlessResolveStage;
+import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependency;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import static com.azure.tools.bomgenerator.Utils.RESOLVED_EXCLUSION_LIST;
+import static com.azure.tools.bomgenerator.Utils.toBomDependencyNoVersion;
+
+public class DependencyAnalyzer {
+ private Set inputDependencies = new HashSet<>();
+ private Set externalDependencies = new HashSet<>();
+ private Set bomEligibleDependencies = new HashSet<>();
+ private Set bomIneligibleDependencies = new HashSet<>();
+ private Map coreDependencyNameToDependency = new HashMap<>();
+ private Map errorInfo = new HashMap();
+
+ private Map>> nameToVersionToChildrenDependencyTree = new TreeMap<>(new Comparator() {
+ @Override
+ public int compare(BomDependencyNoVersion o1, BomDependencyNoVersion o2) {
+ return (o1.getGroupId() + o1.getArtifactId()).compareTo(o1.getGroupId() + o2.getArtifactId());
+ }
+ });
+ private static Logger logger = LoggerFactory.getLogger(BomGenerator.class);
+
+ DependencyAnalyzer(Collection inputDependencies, Collection externalDependencies) {
+ if (inputDependencies != null) {
+ this.inputDependencies.addAll(inputDependencies);
+ }
+ if (externalDependencies != null) {
+ this.externalDependencies.addAll(externalDependencies);
+ }
+ }
+
+ public Collection getBomEligibleDependencies() {
+ return this.bomEligibleDependencies;
+ }
+
+ public void reduce() {
+ analyze();
+ generateReport();
+ this.bomEligibleDependencies.retainAll(this.inputDependencies);
+ }
+
+ public boolean validate() {
+ analyze();
+ return nameToVersionToChildrenDependencyTree.values().stream().anyMatch(value -> value.size() > 1);
+ }
+
+ private void analyze() {
+ pickCoreDependencyRoots();
+ resolveTree();
+ resolveConflicts();
+ filterConflicts();
+ }
+
+ private void generateReport() {
+ // From the input assemblies find the ones that have been dropped, along with why?
+ Set droppedDependencies = inputDependencies.stream().filter(dependency -> bomIneligibleDependencies.contains(dependency)).collect(Collectors.toSet());
+ if (droppedDependencies.size() == 0) {
+ return;
+ }
+
+ if (errorInfo.size() > 0) {
+ errorInfo.keySet().stream().forEach(key -> {
+ if (droppedDependencies.contains(key)) {
+ var conflictingDependencies = errorInfo.get(key).getConflictingDependencies();
+ var expectedDependency = errorInfo.get(key).getExpectedDependency();
+ if (expectedDependency != null) {
+ logger.info("Dropped dependency {}.", key.toString(), expectedDependency);
+ }
+
+ conflictingDependencies.stream().forEach(conflictingDependency ->
+ logger.info("\t\tIncludes dependency {}. Expected dependency {}", conflictingDependency.getActualDependency(), conflictingDependency.getExpectedDependency()));
+ }
+ });
+ }
+ }
+
+ private BomDependency getAzureCoreDependencyFromInput() {
+ return inputDependencies.stream().filter(dependency -> dependency.getArtifactId().equals("azure-core")).findFirst().get();
+ }
+
+ private void pickCoreDependencyRoots() {
+ BomDependency coreDependency = getAzureCoreDependencyFromInput();
+
+ // Get all the dependencies of the azure-core dependency and put them in the coreDependencyMap.
+ var coreDependencies = getDependencies(coreDependency);
+ coreDependencyNameToDependency.put(toBomDependencyNoVersion(coreDependency), coreDependency);
+ coreDependencies.forEach(dependency -> coreDependencyNameToDependency.put(toBomDependencyNoVersion(dependency), dependency));
+
+ // Put all the core dependencies which are not external dependencies in the bomEligible list.
+ for(var dependency : coreDependencyNameToDependency.values()) {
+ if(!externalDependencies.contains(dependency)) {
+ // If this is not an external dependency, it needs to be in the BOM.
+ bomEligibleDependencies.add(dependency);
+ }
+ }
+ }
+
+
+ /* Create a tree map of all the input binaries into the following map.
+ * {groupId_artifactId}: {v1} : {all ancestors that include this binary.}
+ * : {v2} : {all ancestors that include this binary.}
+ * : {v3} : {all ancestors that include this binary.}
+ */
+ private void resolveTree() {
+ for (MavenDependency gaLibrary : inputDependencies) {
+ try {
+
+ BomDependency parentDependency = new BomDependency(gaLibrary.getGroupId(), gaLibrary.getArtifactId(), gaLibrary.getVersion());
+ addDependencyToDependencyTree(parentDependency, null, nameToVersionToChildrenDependencyTree);
+
+ List dependencies = getDependencies(gaLibrary);
+ for (BomDependency dependency : dependencies) {
+ if (dependency.getScope() == ScopeType.TEST) {
+ continue;
+ }
+ if (RESOLVED_EXCLUSION_LIST.contains(dependency.getArtifactId())) {
+ continue;
+ }
+
+ BomDependency childDependency = new BomDependency(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion());
+ addDependencyToDependencyTree(childDependency, parentDependency, nameToVersionToChildrenDependencyTree);
+ }
+ } catch (Exception ex) {
+ System.out.println(ex);
+ }
+ }
+ }
+
+ private static List getDependencies(MavenDependency dependency) {
+ try {
+
+ MavenResolvedArtifact mavenResolvedArtifact = null;
+
+ mavenResolvedArtifact = getMavenResolver()
+ .addDependency(dependency)
+ .resolve()
+ .withoutTransitivity()
+ .asSingleResolvedArtifact();
+
+ return Arrays.stream(mavenResolvedArtifact.getDependencies()).map(mavenDependency ->
+ new BomDependency(mavenDependency.getCoordinate().getGroupId(),
+ mavenDependency.getCoordinate().getArtifactId(),
+ mavenDependency.getCoordinate().getVersion(),
+ mavenDependency.getScope())).collect(Collectors.toList());
+ } catch (Exception ex) {
+ logger.error(ex.toString());
+ }
+
+ return new ArrayList<>();
+ }
+
+ private static MavenResolverSystemBase getMavenResolver() {
+ return Maven.configureResolver().withMavenCentralRepo(true);
+ }
+
+ private static void addDependencyToDependencyTree(BomDependency dependency, BomDependency parentDependency, Map>> dependencyTree) {
+ dependencyTree.computeIfAbsent(new BomDependencyNoVersion(dependency.getGroupId(), dependency.getArtifactId()), key -> new HashMap<>());
+
+ var value = dependencyTree.get(dependency).computeIfAbsent(dependency.getVersion(), key -> new ArrayList<>());
+ if(parentDependency != null) {
+ value.add(parentDependency);
+ }
+ }
+
+ private void updateErrorInfo(BomDependency droppedDependency, String expectedVersion) {
+ if (!errorInfo.containsKey(droppedDependency)) {
+ errorInfo.put(droppedDependency, new BomDependencyErrorInfo(new BomDependency(droppedDependency.getGroupId(), droppedDependency.getArtifactId(), expectedVersion)));
+ }
+ }
+
+ private void updateErrorInfo(BomDependency droppedDependency, BomDependency actualDependency, String expectedVersion) {
+ updateErrorInfo(droppedDependency, expectedVersion);
+ errorInfo.get(droppedDependency).addConflictingDependency(actualDependency, new BomDependency(actualDependency.getGroupId(), actualDependency.getArtifactId(), expectedVersion));
+ }
+
+
+ private void makeDependencyInEligible(BomDependency dependency, BomDependency dependencyReason, String expectedVersion) {
+ if (nameToVersionToChildrenDependencyTree.containsKey(dependency)) {
+ HashMap> versionToDependency = nameToVersionToChildrenDependencyTree.get(dependency);
+ bomIneligibleDependencies.add(dependency);
+ if (dependencyReason == null) {
+ dependencyReason = dependency;
+ updateErrorInfo(dependency, expectedVersion);
+ } else {
+ updateErrorInfo(dependency, dependencyReason, expectedVersion);
+ }
+
+ // Make all the dependencies that include these also ineligible.
+ BomDependency finalDependencyReason = dependencyReason;
+ versionToDependency.get(dependency.getVersion()).forEach(parent -> makeDependencyInEligible(parent, finalDependencyReason, expectedVersion));
+ }
+ }
+
+ private void resolveConflict(BomDependencyNoVersion dependencyNoVersion) {
+ Map> versionToDependency = nameToVersionToChildrenDependencyTree.get(dependencyNoVersion);
+ if (versionToDependency.size() > 1) {
+ List versionList = versionToDependency.keySet().stream().sorted(new DependencyVersionComparator()).collect(Collectors.toList());
+ String eligibleVersion;
+
+ logger.trace("Multiple version of the dependency {} included", dependencyNoVersion);
+
+ // 1. We give preference to the dependency version that is included by azure-core.
+ if (coreDependencyNameToDependency.containsKey(dependencyNoVersion)) {
+ eligibleVersion = coreDependencyNameToDependency.get(dependencyNoVersion).getVersion();
+ logger.trace(String.format("\tPicking the version used by azure-core - %s:%s", dependencyNoVersion, eligibleVersion));
+ } else {
+ eligibleVersion = versionList.get(versionList.size() - 1);
+ logger.trace(String.format("\tPicking the latest version %s:%s", dependencyNoVersion, eligibleVersion));
+ }
+
+ BomDependency dependency = new BomDependency(dependencyNoVersion.getGroupId(), dependencyNoVersion.getArtifactId(), eligibleVersion);
+ if (!externalDependencies.contains(dependency)) {
+ bomEligibleDependencies.add(dependency);
+ }
+
+ // All the other versions of this library are made ineligible.
+ for (String version : versionList) {
+ if (!version.equals(eligibleVersion)) {
+ makeDependencyInEligible(new BomDependency(dependency.getGroupId(), dependency.getArtifactId(), version), null, eligibleVersion);
+ }
+ }
+ }
+ }
+
+ private void resolveConflicts() {
+ nameToVersionToChildrenDependencyTree.keySet().stream().forEach(this::resolveConflict);
+ bomEligibleDependencies.removeAll(bomIneligibleDependencies);
+ }
+
+ private void filterConflicts() {
+ nameToVersionToChildrenDependencyTree.keySet().stream().forEach(
+ key -> {
+ HashMap> versionToDependency = nameToVersionToChildrenDependencyTree.get(key);
+
+ if (versionToDependency.size() == 1) {
+ BomDependency dependency = new BomDependency(key.getGroupId(), key.getArtifactId(), versionToDependency.keySet().stream().findFirst().get());
+ if (!bomIneligibleDependencies.contains(dependency)
+ && !externalDependencies.contains(dependency)) {
+ // No conflict, the library can be added to the list.
+ bomEligibleDependencies.add(dependency);
+ }
+ }
+ });
+ }
+}
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyComparator.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyComparator.java
new file mode 100644
index 0000000000000..71f2c0208c26c
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyComparator.java
@@ -0,0 +1,18 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator;
+
+import org.apache.maven.model.Dependency;
+
+import java.util.Comparator;
+
+public class DependencyComparator implements Comparator {
+ @Override
+ public int compare(Object o1, Object o2) {
+ Dependency dependency1 = (Dependency) o1;
+ Dependency dependency2 = (Dependency) o2;
+
+ return dependency1.toString().compareTo(dependency2.toString());
+ }
+}
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyVersionComparator.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyVersionComparator.java
new file mode 100644
index 0000000000000..8c5300ba515af
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyVersionComparator.java
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator;
+
+import org.apache.maven.artifact.versioning.ComparableVersion;
+
+import java.util.Comparator;
+
+public class DependencyVersionComparator implements Comparator {
+
+ @Override
+ public int compare(String o1, String o2) {
+ ComparableVersion version1 = new ComparableVersion(o1);
+ ComparableVersion version2 = new ComparableVersion(o2);
+
+ return version1.compareTo(version2);
+ }
+}
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Main.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Main.java
new file mode 100644
index 0000000000000..68589c34cca7f
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Main.java
@@ -0,0 +1,64 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator;
+
+import java.util.regex.Matcher;
+
+import static com.azure.tools.bomgenerator.Utils.BASE_AZURE_GROUPID;
+import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_EXTERNALDEPENDENCIES;
+import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_GROUPID;
+import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_INPUTFILE;
+import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_OUTPUTFILE;
+import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_POMFILE;
+
+public class Main {
+
+ public static void main(String[] args) {
+ BomGenerator generator = parseCommandLine(args);
+ generator.generate();
+ }
+
+ private static BomGenerator parseCommandLine(String[] args) {
+ String inputFile = null, outputFile = null, pomFile = null;
+ for (String arg : args) {
+ Matcher matcher = Utils.COMMANDLINE_REGEX.matcher(arg);
+ if (matcher.matches()) {
+ if (matcher.groupCount() == 2) {
+ String argName = matcher.group(1);
+ String argValue = matcher.group(2);
+
+ switch (argName.toLowerCase()) {
+ case COMMANDLINE_INPUTFILE:
+ inputFile = argValue;
+ break;
+
+ case COMMANDLINE_OUTPUTFILE:
+ outputFile = argValue;
+ break;
+
+ case COMMANDLINE_POMFILE:
+ pomFile = argValue;
+ break;
+ }
+ }
+ }
+ }
+
+ // validate that each of these are present.
+ validateInputs(inputFile, outputFile, pomFile);
+ return new BomGenerator(inputFile, outputFile, pomFile);
+ }
+
+ private static void validateInputs(String inputFile, String outputFile, String pomFile) {
+ validateInput(inputFile, COMMANDLINE_INPUTFILE);
+ validateInput(outputFile, COMMANDLINE_OUTPUTFILE);
+ validateInput(pomFile, COMMANDLINE_POMFILE);
+ }
+
+ private static void validateInput(String argName, String argValue) {
+ if(argValue == null || argValue.isEmpty()) {
+ throw new NullPointerException(String.format("%s can't be null", argName));
+ }
+ }
+}
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Utils.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Utils.java
new file mode 100644
index 0000000000000..0433ac49d470c
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Utils.java
@@ -0,0 +1,139 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator;
+
+import com.azure.tools.bomgenerator.models.BomDependency;
+import com.azure.tools.bomgenerator.models.BomDependencyNoVersion;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.DependencyManagement;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class Utils {
+ public static final String COMMANDLINE_INPUTFILE = "inputfile";
+ public static final String COMMANDLINE_OUTPUTFILE = "outputfile";
+ public static final String COMMANDLINE_POMFILE = "pomfile";
+ public static final String COMMANDLINE_EXTERNALDEPENDENCIES = "externalDependencies";
+ public static final String COMMANDLINE_GROUPID = "groupid";
+ public static final Pattern COMMANDLINE_REGEX = Pattern.compile("-(.*)=(.*)");
+ public static final List EXCLUSION_LIST = Arrays.asList("azure-spring-data-cosmos", "azure-spring-data-cosmos-test", "azure-core-test", "azure-sdk-all", "azure-sdk-parent", "azure-client-sdk-parent");
+ public static final Pattern SDK_DEPENDENCY_PATTERN = Pattern.compile("com.azure:(.+);(.+);(.+)");
+ public static final String BASE_AZURE_GROUPID = "com.azure";
+ public static final String AZURE_TEST_LIBRARY_IDENTIFIER = "-test";
+ public static final String AZURE_PERF_LIBRARY_IDENTIFIER = "-perf";
+ public static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
+ public static final Pattern STRING_SPLIT_BY_DOT = Pattern.compile("[.]");
+
+ public static final HashSet RESOLVED_EXCLUSION_LIST = new HashSet<>(Arrays.asList(
+ "junit-jupiter-api"
+ ));
+
+ public static final String POM_TYPE = "pom";
+ private static Logger logger = LoggerFactory.getLogger(Utils.class);
+
+ public static List getExternalDependenciesContent(List dependencies) {
+ List allResolvedDependencies = new ArrayList<>();
+
+ for (Dependency dependency : dependencies) {
+ List resolvedDependencies = getPomFileContent(dependency);
+
+ if (resolvedDependencies != null) {
+ allResolvedDependencies.addAll(resolvedDependencies);
+ }
+ }
+
+ return allResolvedDependencies;
+ }
+
+ public static List getPomFileContent(Dependency dependency) {
+ String[] groups = STRING_SPLIT_BY_DOT.split(dependency.getGroupId());
+ String url = null;
+ if(groups.length == 2) {
+ url = "https://repo1.maven.org/maven2" + "/" + groups[0] + "/" + groups[1] + "/" + dependency.getArtifactId() + "/" + dependency.getVersion() + "/" + dependency.getArtifactId() + "-" + dependency.getVersion() + ".pom";
+ }
+ else if (groups.length == 3) {
+ url = "https://repo1.maven.org/maven2" + "/" + groups[0] + "/" + groups[1] + "/" + groups[2] + "/" + dependency.getArtifactId() + "/" + dependency.getVersion() + "/" + dependency.getArtifactId() + "-" + dependency.getVersion() + ".pom";
+ }
+ else {
+ throw new UnsupportedOperationException("Can't parse the external BOM file.");
+ }
+
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .GET()
+ .header("accept", "application/xml")
+ .timeout(Duration.ofMillis(5000))
+ .build();
+
+ return HTTP_CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream())
+ .thenApply(response -> {
+ if(response.statusCode() == 200) {
+ return Utils.parsePomFileContent(response.body());
+ }
+
+ return null;
+ }).join();
+ }
+
+ public static BomDependencyNoVersion toBomDependencyNoVersion(BomDependency bomDependency) {
+ return new BomDependencyNoVersion(bomDependency.getGroupId(), bomDependency.getArtifactId());
+ }
+
+ private static List parsePomFileContent(InputStream responseStream) {
+ MavenXpp3Reader reader = new MavenXpp3Reader();
+ try {
+ Model model = reader.read(responseStream);
+ DependencyManagement management = model.getDependencyManagement();
+
+ return management.getDependencies().stream().map(dep -> {
+ String version = getPropertyName(dep.getVersion());
+
+ while(model.getProperties().getProperty(version) != null) {
+ version = getPropertyName(model.getProperties().getProperty(version));
+ }
+
+ if(version == null) {
+ version = dep.getVersion();
+ }
+
+ BomDependency bomDependency = new BomDependency(dep.getGroupId(), dep.getArtifactId(), version);
+ return bomDependency;
+ }).collect(Collectors.toList());
+ } catch (IOException exception) {
+ exception.printStackTrace();
+ } catch (XmlPullParserException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ private static String getPropertyName(String propertyValue) {
+ if(propertyValue.startsWith("${")) {
+ return propertyValue.substring(2, propertyValue.length() - 1);
+ }
+
+ return propertyValue;
+ }
+}
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependency.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependency.java
new file mode 100644
index 0000000000000..898377b3df084
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependency.java
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator.models;
+
+import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
+
+public class BomDependency extends BomDependencyNoVersion {
+ private String version;
+ private ScopeType scope;
+
+ public BomDependency(String groupId, String artifactId, String version) {
+ super(groupId, artifactId);
+ this.version = version;
+ this.scope = null;
+ }
+
+ public BomDependency(String groupId, String artifactId, String version, ScopeType scope) {
+ this(groupId, artifactId, version);
+ this.scope = scope;
+ }
+
+ @Override
+ public String getVersion() {
+ return this.version;
+ }
+
+ public ScopeType getScope() {
+ return this.scope;
+ }
+
+ @Override
+ public String toString() {
+ return this.getGroupId() + ":" + this.getArtifactId() + ":" + this.getVersion();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(o == null) {
+ return false;
+ }
+
+ if (o instanceof BomDependency) {
+ BomDependency bomSource = this;
+ BomDependency bomTarget = (BomDependency) o;
+
+ return bomSource.toString().equals(bomTarget.toString());
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+}
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyErrorInfo.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyErrorInfo.java
new file mode 100644
index 0000000000000..2aa7e33b5cbfc
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyErrorInfo.java
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator.models;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class BomDependencyErrorInfo {
+ private BomDependency expectedDependency;
+ private Set conflictingDependencies;
+
+ public BomDependencyErrorInfo(BomDependency expectedDependency) {
+ this.expectedDependency = expectedDependency;
+ conflictingDependencies = new HashSet<>();
+ }
+
+ public void addConflictingDependency(BomDependency actualDependency, BomDependency expectedDependency) {
+ conflictingDependencies.add(new ConflictingDependency(actualDependency, expectedDependency));
+ }
+
+ public Set getConflictingDependencies() {
+ return Collections.unmodifiableSet(this.conflictingDependencies);
+ }
+
+ public BomDependency getExpectedDependency() {
+ return this.expectedDependency;
+ }
+}
+
+
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyNoVersion.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyNoVersion.java
new file mode 100644
index 0000000000000..2da94b98efc9e
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyNoVersion.java
@@ -0,0 +1,98 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator.models;
+
+import org.jboss.shrinkwrap.resolver.api.maven.PackagingType;
+import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenCoordinate;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependency;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependencyExclusion;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class BomDependencyNoVersion implements MavenDependency {
+ private String groupId;
+ private String artifactId;
+
+ public BomDependencyNoVersion(String groupId, String artifactId) {
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ }
+
+ public BomDependencyNoVersion(MavenCoordinate coordinate) {
+ this.artifactId = coordinate.getArtifactId();
+ this.groupId = coordinate.getGroupId();
+ }
+
+ @Override
+ public Set getExclusions() {
+ return new HashSet<>();
+ }
+
+ @Override
+ public ScopeType getScope() {
+ return ScopeType.RUNTIME;
+ }
+
+ @Override
+ public boolean isOptional() {
+ return false;
+ }
+
+ @Override
+ public PackagingType getPackaging() {
+ return PackagingType.JAR;
+ }
+
+ @Override
+ public PackagingType getType() {
+ return PackagingType.JAR;
+ }
+
+ @Override
+ public String getClassifier() {
+ return null;
+ }
+
+ @Override
+ public String getVersion() {
+ return null;
+ }
+
+ @Override
+ public String getGroupId() {
+ return groupId;
+ }
+
+ @Override
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ @Override
+ public String toCanonicalForm() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return this.getGroupId() + ":" + this.getArtifactId();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(o instanceof BomDependencyNoVersion) {
+ BomDependencyNoVersion bomDependency = (BomDependencyNoVersion) o;
+ return bomDependency.toString().equals(this.toString());
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+}
diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/ConflictingDependency.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/ConflictingDependency.java
new file mode 100644
index 0000000000000..5f45714607974
--- /dev/null
+++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/ConflictingDependency.java
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.bomgenerator.models;
+
+public class ConflictingDependency {
+ private BomDependency expectedDependency;
+ private BomDependency actualDependency;
+
+ ConflictingDependency(BomDependency actualDependency, BomDependency expectedDependency) {
+ this.expectedDependency = expectedDependency;
+ this.actualDependency = actualDependency;
+ }
+
+ ConflictingDependency(BomDependency actualDependency) {
+ this.actualDependency = actualDependency;
+ }
+
+ public BomDependency getExpectedDependency() {
+ return this.expectedDependency;
+ }
+
+ public BomDependency getActualDependency() {
+ return this.actualDependency;
+ }
+
+ @Override
+ public String toString() {
+ return this.actualDependency.toString() + this.expectedDependency.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(o == null) {
+ return false;
+ }
+
+ if (o instanceof ConflictingDependency) {
+ ConflictingDependency bomSource = this;
+ ConflictingDependency bomTarget = (ConflictingDependency) o;
+
+ return bomSource.toString().equals(bomTarget.toString());
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+}
diff --git a/eng/bomgenerator/src/main/resources/log4j.properties b/eng/bomgenerator/src/main/resources/log4j.properties
new file mode 100644
index 0000000000000..6f3e271cbf56e
--- /dev/null
+++ b/eng/bomgenerator/src/main/resources/log4j.properties
@@ -0,0 +1,17 @@
+# Define the root logger with appender file
+log4j.rootLogger = INFO, FILE
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.Target=System.out
+
+# Define the file appender
+log4j.appender.FILE=org.apache.log4j.FileAppender
+log4j.appender.FILE.File=report.log
+
+# Set the append to false, overwrite
+log4j.appender.FILE.Append=false
+
+# Define the layout for file appender
+log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
+log4j.appender.FILE.layout.conversionPattern=%m%n