Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize maven on background #468

Merged
merged 1 commit into from
Jul 28, 2023
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,21 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat Inc. and others.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.lemminx.extensions.maven;

import java.util.concurrent.CancellationException;

/**
* Exception thrown when Maven is initializing.
*
* @author Angelo ZERR
*
*/
public class MavenInitializationException extends CancellationException{

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -108,7 +110,10 @@
*
*/
public class MavenLemminxExtension implements IXMLExtension {


// Used for tests
public static boolean initializeMavenOnBackground = true;

private static final Logger LOGGER = Logger.getLogger(MavenLemminxExtension.class.getName());
private static final String MAVEN_XMLLS_EXTENSION_REALM_ID = MavenLemminxExtension.class.getName();

Expand Down Expand Up @@ -136,6 +141,9 @@ public class MavenLemminxExtension implements IXMLExtension {
private URIResolverExtensionManager resolverExtensionManager;
private List<WorkspaceFolder> initialWorkspaceFolders = List.of();
private LinkedHashSet<URI> currentWorkspaceFolders = new LinkedHashSet<>();

// Thread which loads Maven component (plexus container, maven session, etc) which can take some time.
private CompletableFuture<Void> mavenInitializer;

@Override
public void doSave(ISaveContext context) {
Expand Down Expand Up @@ -189,32 +197,51 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
}
}

private synchronized void initialize() {
if (mavenSession != null) {
// already initialized
return;
private void initialize() throws MavenInitializationException {
if (!getMavenInitializer().isDone() ) {
// The Maven initialization is not ready, throws a MavenInitializationException.
throw new MavenInitializationException();
}
try {
this.container = newPlexusContainer();
mavenRequest = initMavenRequest(container, settings);
DefaultRepositorySystemSessionFactory repositorySessionFactory = container.lookup(DefaultRepositorySystemSessionFactory.class);
RepositorySystemSession repositorySystemSession = repositorySessionFactory.newRepositorySession(mavenRequest);
MavenExecutionResult mavenResult = new DefaultMavenExecutionResult();
// TODO: MavenSession is deprecated. Investigate for alternative
mavenSession = new MavenSession(container, repositorySystemSession, mavenRequest, mavenResult);
cache = new MavenProjectCache(this);
localRepositorySearcher = new LocalRepositorySearcher(mavenSession.getRepositorySession().getLocalRepository().getBasedir());
if (!settings.getCentral().isSkip()) {
centralSearcher = new RemoteCentralRepositorySearcher();
}

private synchronized CompletableFuture<Void> getMavenInitializer() {
// Create thread which loads Maven component (plexus container, maven session, etc) which can take some time.
// We do this initialization on background to avoid breaking the XML syntax validation, XML based onXSD, XML completion based on XSD
// while Maven component is initializing.
if (mavenInitializer == null) {
mavenInitializer = CompletableFuture.runAsync(() -> {
try {
this.container = newPlexusContainer();
mavenRequest = initMavenRequest(container, settings);
DefaultRepositorySystemSessionFactory repositorySessionFactory = container.lookup(DefaultRepositorySystemSessionFactory.class);
RepositorySystemSession repositorySystemSession = repositorySessionFactory.newRepositorySession(mavenRequest);
MavenExecutionResult mavenResult = new DefaultMavenExecutionResult();
// TODO: MavenSession is deprecated. Investigate for alternative
mavenSession = new MavenSession(container, repositorySystemSession, mavenRequest, mavenResult);
cache = new MavenProjectCache(mavenSession);
localRepositorySearcher = new LocalRepositorySearcher(mavenSession.getRepositorySession().getLocalRepository().getBasedir());
if (!settings.getCentral().isSkip()) {
centralSearcher = new RemoteCentralRepositorySearcher();
}
buildPluginManager = null;
mavenPluginManager = container.lookup(MavenPluginManager.class);
buildPluginManager = container.lookup(BuildPluginManager.class);
internalDidChangeWorkspaceFolders(this.initialWorkspaceFolders.stream().map(WorkspaceFolder::getUri).map(URI::create).toArray(URI[]::new), null);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
stop(currentRegistry);
}
});
}
if (!initializeMavenOnBackground) {
// This code is for tests
try {
mavenInitializer.get();
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
}
buildPluginManager = null;
mavenPluginManager = container.lookup(MavenPluginManager.class);
buildPluginManager = container.lookup(BuildPluginManager.class);
didChangeWorkspaceFolders(this.initialWorkspaceFolders.stream().map(WorkspaceFolder::getUri).map(URI::create).toArray(URI[]::new), null);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
stop(currentRegistry);
}
return mavenInitializer;
}

private MavenExecutionRequest initMavenRequest(PlexusContainer container, XMLMavenSettings options) throws Exception {
Expand Down Expand Up @@ -388,6 +415,9 @@ public void stop(XMLExtensionsRegistry registry) {
}
this.mavenSession = null;
this.currentRegistry = null;
if (mavenInitializer != null) {
mavenInitializer.cancel(true);
}
}

public static boolean match(DOMDocument document) {
Expand Down Expand Up @@ -449,9 +479,17 @@ public URIResolverExtensionManager getUriResolveExtentionManager() {
public LinkedHashSet<URI> getCurrentWorkspaceFolders() {
return currentWorkspaceFolders;
}

public void didChangeWorkspaceFolders(URI[] added, URI[] removed) {
initialize();
CompletableFuture<Void> initializer = getMavenInitializer();
if (initializer.isDone()) {
internalDidChangeWorkspaceFolders(added, removed);
} else {
initializer.thenAccept(Void -> internalDidChangeWorkspaceFolders(added, removed));
}
}

public void internalDidChangeWorkspaceFolders(URI[] added, URI[] removed) {
currentWorkspaceFolders.addAll(List.of(added != null? added : new URI[0]));
currentWorkspaceFolders.removeAll(List.of(removed != null ? removed : new URI[0]));
WorkspaceReader workspaceReader = mavenRequest.getWorkspaceReader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
*******************************************************************************/
package org.eclipse.lemminx.extensions.maven;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -19,6 +18,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand All @@ -28,6 +28,7 @@
import org.apache.maven.artifact.repository.MavenArtifactRepository;
import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Build;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.DefaultModelProblem;
Expand All @@ -49,6 +50,7 @@
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.extensions.maven.utils.DOMModelSource;

public class MavenProjectCache {

Expand All @@ -58,19 +60,15 @@ public class MavenProjectCache {
private final Map<URI, MavenProject> projectCache;
private final Map<URI, Collection<ModelProblem>> problemCache;

private final MavenLemminxExtension lemminxMavenPlugin;
private final PlexusContainer plexusContainer;
private final MavenExecutionRequest mavenRequest;
private final MavenSession mavenSession;

MavenXpp3Reader mavenReader = new MavenXpp3Reader();
private ProjectBuilder projectBuilder;

private final List<Consumer<MavenProject>> projectParsedListeners = new ArrayList<>();

public MavenProjectCache(MavenLemminxExtension lemminxMavenPlugin) {
this.lemminxMavenPlugin = lemminxMavenPlugin;
this.mavenRequest = lemminxMavenPlugin.getMavenSession().getRequest();
this.plexusContainer = lemminxMavenPlugin.getPlexusContainer();
public MavenProjectCache(MavenSession mavenSession) {
this.mavenSession = mavenSession;
this.lastCheckedVersion = new HashMap<>();
this.projectCache = new HashMap<>();
this.problemCache = new HashMap<>();
Expand Down Expand Up @@ -212,7 +210,11 @@ private void parseAndCache(URI uri, int version, FileModelSource source) {
}
}
}
} catch (Exception e) {
} catch (CancellationException e){
// The document which has been used to load Maven project is out of dated
throw e;
}
catch (Exception e) {
// Do not add any info, like lastCheckedVersion or problems, to the cache
// In case of project/problems etc. is not available due to an exception happened.
//
Expand All @@ -236,12 +238,7 @@ private void cacheProject(final MavenProject project, File file, URI uri,
private void parseAndCache(DOMDocument document) {
URI uri = URI.create(document.getDocumentURI()).normalize();
int version = document.getTextDocument().getVersion();
FileModelSource source = new FileModelSource(new File(uri)) {
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(document.getText().getBytes());
}
};
DOMModelSource source = new DOMModelSource(document);
parseAndCache(uri, version, source);
}

Expand All @@ -257,12 +254,13 @@ private ProjectBuildingRequest newProjectBuildingRequest() {

private ProjectBuildingRequest newProjectBuildingRequest(boolean resolveDependencies) {
ProjectBuildingRequest request = new DefaultProjectBuildingRequest();
MavenExecutionRequest mavenRequest = mavenSession.getRequest();
request.setSystemProperties(mavenRequest.getSystemProperties());
request.setLocalRepository(mavenRequest.getLocalRepository());
request.setRemoteRepositories(mavenRequest.getRemoteRepositories());
request.setPluginArtifactRepositories(mavenRequest.getPluginArtifactRepositories());
// TODO more to transfer from mavenRequest to ProjectBuildingRequest?
request.setRepositorySession(lemminxMavenPlugin.getMavenSession().getRepositorySession());
request.setRepositorySession(mavenSession.getRepositorySession());
request.setResolveDependencies(resolveDependencies);

// See: https://issues.apache.org/jira/browse/MRESOLVER-374
Expand All @@ -283,7 +281,7 @@ private void initializeMavenBuildState() {
}

public PlexusContainer getPlexusContainer() {
return plexusContainer;
return mavenSession.getContainer();
}

public Collection<MavenProject> getProjects() {
Expand All @@ -301,12 +299,11 @@ public MavenProject getSnapshotProject(DOMDocument document, String profileId, b
request.setActiveProfileIds(List.of(profileId));
}
try {
return projectBuilder.build(new FileModelSource(new File(document.getDocumentURI())) {
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(document.getText().getBytes());
}
}, request).getProject();
DOMModelSource source = new DOMModelSource(document);
return projectBuilder.build(source, request).getProject();
} catch (CancellationException e) {
// The document which has been used to load Maven project is out of dated
throw e;
} catch (ProjectBuildingException e) {
List<ProjectBuildingResult> result = e.getResults();
if (result != null && result.size() == 1 && result.get(0).getProject() != null) {
Expand Down
Loading