Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com).
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* WSO2 LLC - support for WSO2 Micro Integrator Configuration
*/

package org.eclipse.lemminx.customservice.synapse.parser;

import java.util.List;

/**
* Encapsulates the results of connector dependency download operations.
* <p>
* Contains two lists: connectors that failed to download due to general errors,
* and connectors that were skipped because they are already provided by an
* integration project dependency.
* </p>
*/
public class ConnectorDependencyDownloadResult {

private List<String> failedDependencies;
private List<String> fromIntegrationProjectDependencies;

public ConnectorDependencyDownloadResult(List<String> failedDependencies,
List<String> fromIntegrationProjectDependencies) {
this.failedDependencies = failedDependencies;
this.fromIntegrationProjectDependencies = fromIntegrationProjectDependencies;
}

public List<String> getFailedDependencies() {
return failedDependencies;
}

public List<String> getFromIntegrationProjectDependencies() {
return fromIntegrationProjectDependencies;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ public class ConnectorDownloadManager {

private static final Logger LOGGER = Logger.getLogger(ConnectorDownloadManager.class.getName());

public static List<String> downloadDependencies(String projectPath, List<DependencyDetails> dependencies) {
public static ConnectorDependencyDownloadResult downloadDependencies(String projectPath, List<DependencyDetails> dependencies) {

LOGGER.log(Level.INFO, "Starting connector dependency download for project: " + new File(projectPath).getName()
+ " with " + dependencies.size() + " dependencies");
String projectId = new File(projectPath).getName() + "_" + Utils.getHash(projectPath);
Comment thread
chathuranga-jayanath-99 marked this conversation as resolved.
File directory = Path.of(System.getProperty(Constant.USER_HOME), Constant.WSO2_MI, Constant.CONNECTORS,
projectId).toFile();
Expand All @@ -80,8 +82,17 @@ public static List<String> downloadDependencies(String projectPath, List<Depende

deleteRemovedConnectors(downloadDirectory, dependencies, projectPath);
List<String> failedDependencies = new ArrayList<>();
List<String> fromIntegrationProjectDependencies = new ArrayList<>();

for (DependencyDetails dependency : dependencies) {
String dependencyId =
dependency.getGroupId() + Constant.HYPHEN + dependency.getArtifact() + Constant.HYPHEN + dependency.getVersion();
if (isConnectorFromIntegrationProjectDependency(dependency.getArtifact())) {
LOGGER.log(Level.WARNING, "Connector " + dependencyId +
" is provided by an integration project dependency. Download not allowed.");
fromIntegrationProjectDependencies.add(dependencyId);
continue;
}
try {
File connector = Path.of(downloadDirectory.getAbsolutePath(),
dependency.getArtifact() + "-" + dependency.getVersion() + Constant.ZIP_EXTENSION).toFile();
Expand All @@ -98,14 +109,25 @@ public static List<String> downloadDependencies(String projectPath, List<Depende
downloadDirectory, Constant.ZIP_EXTENSION_NO_DOT, projectPath);
}
} catch (Exception e) {
String failedDependency =
dependency.getGroupId() + "-" + dependency.getArtifact() + "-" + dependency.getVersion();
LOGGER.log(Level.WARNING,
"Error occurred while downloading dependency " + failedDependency + ": " + e.getMessage());
failedDependencies.add(failedDependency);
"Error occurred while downloading dependency " + dependencyId + ": " + e.getMessage());
failedDependencies.add(dependencyId);
}
}
return failedDependencies;
LOGGER.log(Level.INFO, "Connector dependency download completed for project: " + new File(projectPath).getName()
+ ". Failed: " + failedDependencies.size() + ", From integration project dependencies: "
+ fromIntegrationProjectDependencies.size());
return new ConnectorDependencyDownloadResult(failedDependencies, fromIntegrationProjectDependencies);
Comment thread
chathuranga-jayanath-99 marked this conversation as resolved.
}

/**
* Returns true if the connector with the given artifact ID is already loaded from an integration
* project dependency (i.e. not owned by the current project).
*/
private static boolean isConnectorFromIntegrationProjectDependency(String artifactId) {

return ConnectorHolder.getInstance().getConnectors().stream()
.anyMatch(c -> artifactId.equalsIgnoreCase(c.getArtifactId()) && !c.isFromProject());
Comment thread
chathuranga-jayanath-99 marked this conversation as resolved.
}

private static void deleteRemovedConnectors(File downloadDirectory, List<DependencyDetails> dependencies,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,19 @@ public static String downloadDependencies(String projectPath) {
pomDetailsResponse.getDependenciesDetails().getConnectorDependencies();
List<DependencyDetails> integrationProjectDependencies =
pomDetailsResponse.getDependenciesDetails().getIntegrationProjectDependencies();
List<String> failedConnectorDependencies =
ConnectorDependencyDownloadResult connectorResult =
ConnectorDownloadManager.downloadDependencies(projectPath, connectorDependencies);
Node isVersionedDeployment = pomDetailsResponse.getBuildDetails().getVersionedDeployment();
boolean isVersionedDeploymentEnabled = isVersionedDeployment != null ?
Boolean.parseBoolean(isVersionedDeployment.getValue()) : false;
DependencyDownloadResult integrationProjectResult =
IntegrationProjectDependencyDownloadResult integrationProjectResult =
IntegrationProjectDownloadManager.downloadDependencies(projectPath, integrationProjectDependencies,
isVersionedDeploymentEnabled);

StringBuilder errorMessage = new StringBuilder();
if (!failedConnectorDependencies.isEmpty()) {
String connectorError = "Some connectors were not downloaded: " + String.join(", ", failedConnectorDependencies);
LOGGER.log(Level.SEVERE, connectorError);
errorMessage.append(connectorError);
String connectorErrorMessage = buildConnectorErrorMessage(connectorResult);
if (!connectorErrorMessage.isEmpty()) {
errorMessage.append(connectorErrorMessage);
}

String integrationProjectsErrorMessage = buildIntegrationProjectsErrorMessage(integrationProjectResult);
Expand Down Expand Up @@ -97,7 +96,7 @@ public static String refetchIntegrationProjectDependencies(String projectPath) {
Node isVersionedDeployment = pomDetailsResponse.getBuildDetails().getVersionedDeployment();
boolean isVersionedDeploymentEnabled = isVersionedDeployment != null ?
Boolean.parseBoolean(isVersionedDeployment.getValue()) : false;
DependencyDownloadResult result =
IntegrationProjectDependencyDownloadResult result =
IntegrationProjectDownloadManager.refetchDependencies(projectPath, integrationProjectDependencies,
isVersionedDeploymentEnabled);

Expand All @@ -110,11 +109,41 @@ public static String refetchIntegrationProjectDependencies(String projectPath) {
}

/**
* Builds a human-readable error string from a {@link DependencyDownloadResult},
* Builds a human-readable error string from a {@link ConnectorDependencyDownloadResult},
* logging and concatenating each category of failure. Returns an empty string if
* there were no failures.
*/
private static String buildConnectorErrorMessage(ConnectorDependencyDownloadResult result) {

StringBuilder errorMessage = new StringBuilder();

if (!result.getFailedDependencies().isEmpty()) {
String connectorError = "Some connectors were not downloaded: " +
String.join(", ", result.getFailedDependencies());
LOGGER.log(Level.SEVERE, connectorError);
errorMessage.append(connectorError);
}

if (!result.getFromIntegrationProjectDependencies().isEmpty()) {
String integrationProjectError = "Following connectors are provided by integration project dependencies" +
" and cannot be downloaded: " +
String.join(", ", result.getFromIntegrationProjectDependencies());
LOGGER.log(Level.SEVERE, integrationProjectError);
if (errorMessage.length() > 0) {
errorMessage.append(". ");
}
errorMessage.append(integrationProjectError);
Comment thread
chathuranga-jayanath-99 marked this conversation as resolved.
}

return errorMessage.toString();
}

/**
* Builds a human-readable error string from a {@link IntegrationProjectDependencyDownloadResult},
* logging and concatenating each category of failure. Returns an empty string if
* there were no failures.
*/
private static String buildIntegrationProjectsErrorMessage(DependencyDownloadResult result) {
private static String buildIntegrationProjectsErrorMessage(IntegrationProjectDependencyDownloadResult result) {

StringBuilder errorMessage = new StringBuilder();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
* Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com).
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
Expand All @@ -17,20 +17,22 @@
import java.util.List;

/**
* Encapsulates the results of dependency download operations.
* Encapsulates the results of integration project dependency download operations.
* <p>
* Contains two lists: dependencies that failed to download due to general errors,
* and dependencies that failed due to missing or invalid descriptor.xml files.
* Contains three lists: dependencies that failed to download due to general errors,
* dependencies that failed due to missing or invalid descriptor.xml files, and
* dependencies whose versioning type does not match the current project.
* </p>
*/
public class DependencyDownloadResult {
public class IntegrationProjectDependencyDownloadResult {

private List<String> failedDependencies;
private List<String> noDescriptorDependencies;
private List<String> versioningTypeMismatchDependencies;

public DependencyDownloadResult(List<String> failedDependencies, List<String> noDescriptorDependencies,
List<String> versioningTypeMismatchDependencies) {
public IntegrationProjectDependencyDownloadResult(List<String> failedDependencies,
List<String> noDescriptorDependencies,
List<String> versioningTypeMismatchDependencies) {
this.failedDependencies = failedDependencies;
this.noDescriptorDependencies = noDescriptorDependencies;
this.versioningTypeMismatchDependencies = versioningTypeMismatchDependencies;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,16 @@ public class IntegrationProjectDownloadManager {
* @param isVersionedDeploymentEnabled indicates if versioned deployment is enabled in the parent project
* @return a result object containing any dependencies that failed to download or process
*/
public static DependencyDownloadResult refetchDependencies(String projectPath, List<DependencyDetails> dependencies,
public static IntegrationProjectDependencyDownloadResult refetchDependencies(String projectPath, List<DependencyDetails> dependencies,
boolean isVersionedDeploymentEnabled) {

LOGGER.log(Level.INFO, "Starting hard refresh of dependencies for project: " + new File(projectPath).getName());
LOGGER.log(Level.INFO, "Starting hard refresh of dependencies for project: " + new File(projectPath).getName()
+ " with " + dependencies.size() + " dependencies");
return refetchDependencies(projectPath, dependencies, isVersionedDeploymentEnabled,
Path.of(System.getProperty(Constant.USER_HOME)));
}

public static DependencyDownloadResult refetchDependencies(String projectPath, List<DependencyDetails> dependencies,
public static IntegrationProjectDependencyDownloadResult refetchDependencies(String projectPath, List<DependencyDetails> dependencies,
boolean isVersionedDeploymentEnabled, Path userHome) {

String projectId = new File(projectPath).getName() + UNDERSCORE + Utils.getHash(projectPath);
Expand Down Expand Up @@ -120,14 +121,14 @@ public static DependencyDownloadResult refetchDependencies(String projectPath, L
* @param isVersionedDeploymentEnabled indicates if versioned deployment is enabled in the parent project
* @return a list of dependency identifiers that failed to download or process
*/
public static DependencyDownloadResult downloadDependencies(String projectPath, List<DependencyDetails> dependencies,
public static IntegrationProjectDependencyDownloadResult downloadDependencies(String projectPath, List<DependencyDetails> dependencies,
boolean isVersionedDeploymentEnabled) {

return downloadDependencies(projectPath, dependencies, isVersionedDeploymentEnabled,
Path.of(System.getProperty(Constant.USER_HOME)));
}

public static DependencyDownloadResult downloadDependencies(String projectPath, List<DependencyDetails> dependencies,
public static IntegrationProjectDependencyDownloadResult downloadDependencies(String projectPath, List<DependencyDetails> dependencies,
boolean isVersionedDeploymentEnabled, Path userHome) {

String projectId = new File(projectPath).getName() + UNDERSCORE + Utils.getHash(projectPath);
Expand Down Expand Up @@ -184,7 +185,11 @@ public static DependencyDownloadResult downloadDependencies(String projectPath,
deleteObsoleteDownloadedFiles(downloadDirectory, expectedBaseNames);
deleteObsoleteExtractedDirs(extractDirectory, expectedBaseNames);

return new DependencyDownloadResult(failedDependencies, noDescriptorDependencies, versioningMismatchDependencies);
LOGGER.log(Level.INFO, "Integration project dependency download completed for project: "
+ new File(projectPath).getName() + ". Failed: " + failedDependencies.size()
+ ", No descriptor: " + noDescriptorDependencies.size()
+ ", Version mismatch: " + versioningMismatchDependencies.size());
return new IntegrationProjectDependencyDownloadResult(failedDependencies, noDescriptorDependencies, versioningMismatchDependencies);
Comment thread
chathuranga-jayanath-99 marked this conversation as resolved.
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@

package org.eclipse.lemminx.synapse.connector.downloader;

import org.eclipse.lemminx.customservice.synapse.connectors.ConnectorHolder;
import org.eclipse.lemminx.customservice.synapse.connectors.entity.Connector;
import org.eclipse.lemminx.customservice.synapse.parser.ConnectorDownloadManager;
import org.eclipse.lemminx.customservice.synapse.parser.DependencyDetails;
import org.eclipse.lemminx.customservice.synapse.parser.ConnectorDependencyDownloadResult;
import org.eclipse.lemminx.customservice.synapse.parser.OverviewPageDetailsResponse;
import org.eclipse.lemminx.customservice.synapse.utils.Utils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
Expand All @@ -29,6 +33,7 @@
import static org.eclipse.lemminx.customservice.synapse.parser.pom.PomParser.getPomDetails;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mockStatic;

Expand All @@ -41,6 +46,15 @@ public class ConnectorDownloadManagerTest {
void setUp() {
connectorDownloadManager = new ConnectorDownloadManager();
utilsMock = mockStatic(Utils.class);
ConnectorHolder.getInstance().clearConnectors();
}

@AfterEach
void tearDown() {
ConnectorHolder.getInstance().clearConnectors();
if (utilsMock != null) {
utilsMock.close();
}
}

@Test
Expand All @@ -52,10 +66,32 @@ void downloadConnectorsWithValidDependencies() {
getPomDetails(projectPath, pomDetailsResponse);
List<DependencyDetails>
connectorDependencies = pomDetailsResponse.getDependenciesDetails().getConnectorDependencies();
List<String> failedDependencies = connectorDownloadManager.downloadDependencies(projectPath, connectorDependencies);
utilsMock.close();
ConnectorDependencyDownloadResult result = ConnectorDownloadManager.downloadDependencies(projectPath, connectorDependencies);

assertEquals(0, result.getFailedDependencies().size());
assertEquals(0, result.getFromIntegrationProjectDependencies().size());
}

@Test
void downloadConnectorsFromIntegrationProjectDependency() {
String path = ConnectorDownloadManagerTest.class.getResource("/synapse/pom.parser/test_pom_parser").getPath();
String projectPath = new File(path).getAbsolutePath();

// Simulate a connector already loaded from an integration project dependency (fromProject = false)
Connector dependencyConnector = new Connector();
dependencyConnector.setArtifactId("mi-connector-http");
dependencyConnector.setFromProject(false);
ConnectorHolder.getInstance().addConnector(dependencyConnector);

OverviewPageDetailsResponse pomDetailsResponse = new OverviewPageDetailsResponse();
getPomDetails(projectPath, pomDetailsResponse);
List<DependencyDetails> connectorDependencies =
pomDetailsResponse.getDependenciesDetails().getConnectorDependencies();

ConnectorDependencyDownloadResult result = ConnectorDownloadManager.downloadDependencies(projectPath, connectorDependencies);

assertEquals(0, failedDependencies.size());
assertTrue(result.getFromIntegrationProjectDependencies().stream().anyMatch(dep -> dep.contains("mi-connector-http")),
"Connector from integration project dependency should be marked as failed");
Comment thread
chathuranga-jayanath-99 marked this conversation as resolved.
}

@Test
Expand All @@ -67,9 +103,8 @@ void downloadConnectorsWithInvalidDependencies() {
getPomDetails(projectPath, pomDetailsResponse);
List<DependencyDetails>
connectorDependencies = pomDetailsResponse.getDependenciesDetails().getConnectorDependencies();
List<String> failedDependencies = connectorDownloadManager.downloadDependencies(projectPath, connectorDependencies);
utilsMock.close();
ConnectorDependencyDownloadResult result = ConnectorDownloadManager.downloadDependencies(projectPath, connectorDependencies);

assertFalse(failedDependencies.isEmpty());
assertFalse(result.getFailedDependencies().isEmpty());
}
}
Loading
Loading