Skip to content
Open
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
Expand Up @@ -24,7 +24,9 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.8.0",
org.springsource.ide.eclipse.commons.core,
org.eclipse.e4.ui.css.swt.theme,
org.eclipse.swt,
com.google.gson
com.google.gson,
org.eclipse.m2e.launching,
org.eclipse.m2e.core
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Export-Package: org.springframework.tooling.ls.eclipse.commons,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@
class="org.springframework.tooling.ls.eclipse.commons.commands.OpenJarEntryInEditor"
commandId="org.springframework.tooling.ls.eclipse.commons.commands.OpenJarEntryInEditor">
</handler>
<handler
class="org.springframework.tooling.ls.eclipse.commons.commands.ExecuteMavenGoalHandler"
commandId="maven.goal.custom">
</handler>
</extension>
<extension
point="org.eclipse.ui.commands">
Expand Down Expand Up @@ -195,6 +199,22 @@
optional="false">
</commandParameter>
</command>
<command
id="maven.goal.custom"
name="Explain with AI">
<commandParameter
id="org.eclipse.lsp4e.path.param"
name="Resource Path (unnecessary, only to make lsp4e happy)"
optional="true"
typeId="org.eclipse.lsp4e.pathParameterType">
</commandParameter>
<commandParameter
id="org.eclipse.lsp4e.command.param"
name="Command id (unnecessary, only to make lsp4e happy)"
optional="true"
typeId="org.eclipse.lsp4e.commandParameterType">
</commandParameter>
</command>
</extension>
<extension
point="org.eclipse.e4.ui.css.swt.theme">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*******************************************************************************
* Copyright (c) 2025 Broadcom, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.ls.eclipse.commons.commands;

import java.nio.file.Paths;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.RefreshTab;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.command.LSPCommandHandler;
import org.eclipse.lsp4j.Command;
import org.eclipse.m2e.actions.MavenLaunchConstants;

import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.IMavenProjectRegistry;
import org.eclipse.m2e.core.project.IProjectConfiguration;

import com.google.gson.Gson;

@SuppressWarnings("restriction")
public class ExecuteMavenGoalHandler extends AbstractHandler {

@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
Command cmd = new Gson().fromJson(event.getParameter(LSPCommandHandler.LSP_COMMAND_PARAMETER_ID), Command.class);
if (cmd != null) {
try {
String pomPath = (String) cmd.getArguments().get(0);
String goal = (String) cmd.getArguments().get(1);
System.out.println("Project: '%s', goal: '%s'".formatted(pomPath, goal));

IResource pomFile = LSPEclipseUtils.findResourceFor(Paths.get(pomPath).toUri());
ILaunchConfiguration launchConfig = createLaunchConfiguration(pomFile.getParent(), goal);

DebugUITools.launch(launchConfig, ILaunchManager.RUN_MODE);
} catch (Exception e) {
throw new ExecutionException("Failed to execute Maven Goal command", e);
}
}
throw new ExecutionException("Maven Goal Execution command is invalid");
}

private ILaunchConfiguration createLaunchConfiguration(IContainer basedir, String goal) throws CoreException {
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType launchConfigurationType = launchManager
.getLaunchConfigurationType(MavenLaunchConstants.LAUNCH_CONFIGURATION_TYPE_ID);

String safeConfigName = launchManager.generateLaunchConfigurationName("Goal `%s`".formatted(goal));

ILaunchConfigurationWorkingCopy workingCopy = launchConfigurationType.newInstance(null, safeConfigName);
workingCopy.setAttribute(MavenLaunchConstants.ATTR_POM_DIR, basedir.getLocation().toOSString());
workingCopy.setAttribute(MavenLaunchConstants.ATTR_GOALS, goal);
workingCopy.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_SCOPE, "${project}"); //$NON-NLS-1$
workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_RECURSIVE, true);

setProjectConfiguration(workingCopy, basedir);

return workingCopy;
}

private void setProjectConfiguration(ILaunchConfigurationWorkingCopy workingCopy, IContainer basedir) {
IMavenProjectRegistry projectManager = MavenPlugin.getMavenProjectRegistry();
IFile pomFile = basedir.getFile(IPath.fromOSString(IMavenConstants.POM_FILE_NAME));
IMavenProjectFacade projectFacade = projectManager.create(pomFile, false, new NullProgressMonitor());
if (projectFacade != null) {
IProjectConfiguration configuration = projectFacade.getConfiguration();

String selectedProfiles = configuration.getSelectedProfiles();
if (selectedProfiles != null && selectedProfiles.length() > 0) {
workingCopy.setAttribute(MavenLaunchConstants.ATTR_PROFILES, selectedProfiles);
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*******************************************************************************
* Copyright (c) 2025 Broadcom, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.util;

public class OS {

public static boolean isWindows() {
String os = System.getProperty("os.name");
if (os != null) {
return os.toLowerCase().indexOf("win") >= 0;
}
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private boolean parentProcessStillRunning() {
return true;
}
String command;
if (isWindows()) {
if (OS.isWindows()) {
command = "cmd /c \"tasklist /FI \"PID eq " + pid + "\" | findstr " + pid + "\"";
} else {
command = "kill -0 " + pid;
Expand All @@ -85,7 +85,7 @@ private boolean parentProcessStillRunning() {
process.destroy();
finished = process.waitFor(POLL_DELAY_SECS, TimeUnit.SECONDS); // wait for the process to stop
}
if (isWindows() && finished && process.exitValue() > 1) {
if (OS.isWindows() && finished && process.exitValue() > 1) {
// the tasklist command should return 0 (parent process exists) or 1 (parent process doesn't exist)
logger.info("The tasklist command: '{}' returns {}", command, process.exitValue());
return true;
Expand All @@ -103,7 +103,7 @@ private boolean parentProcessStillRunning() {
// It is only closed when the Process object is garbage collected (in its finalize() method).
// On Windows, when the Java LS is idle, we need to explicitly request a GC,
// to prevent an accumulation of zombie processes, as finalize() will be called.
if (isWindows()) {
if (OS.isWindows()) {
// Java >= 9 doesn't close the handle when the process is garbage collected
// We need to close the opened streams
if (!isJava1x) {
Expand Down Expand Up @@ -134,11 +134,4 @@ public MessageConsumer apply(final MessageConsumer consumer) {
};
}

private static boolean isWindows() {
String os = System.getProperty("os.name");
if (os != null) {
return os.toLowerCase().indexOf("win") >= 0;
}
return false;
}
}
2 changes: 1 addition & 1 deletion headless-services/commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0-M3</version>
<version>4.0.0-RC2</version>
<relativePath></relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@

import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
Expand Down Expand Up @@ -244,14 +246,14 @@ public static ProjectBuild createProjectBuild(IJavaProject jp, Logger logger) {
// if (facade != null) {
// return ProjectBuild.createMavenBuild(facade.getPom().getLocationURI().toASCIIString());
// } else {
return ProjectBuild.createMavenBuild(jp.getProject().getFile("pom.xml").getLocationURI().toASCIIString());
return ProjectBuild.createMavenBuild(fromIFile(jp.getProject().getFile("pom.xml")).toASCIIString());
// }
} else if (GradleProjectNature.isPresentOn(jp.getProject())) {
IFile g = jp.getProject().getFile("build.gradle");
if (!g.exists()) {
g = jp.getProject().getFile("build.gradle.kts");
}
return ProjectBuild.createGradleBuild(g.exists() ? g.getLocationURI().toASCIIString() : null);
return ProjectBuild.createGradleBuild(g.exists() ? fromIFile(g).toASCIIString() : null);
} else {
try {
for (IClasspathEntry e : jp.getRawClasspath()) {
Expand All @@ -271,20 +273,24 @@ public static ProjectBuild createProjectBuild(IJavaProject jp, Logger logger) {
if (likelyMaven) {
IFile f = jp.getProject().getFile("pom.xml");
if (f.exists()) {
return ProjectBuild.createMavenBuild(f.getLocationURI().toASCIIString());
return ProjectBuild.createMavenBuild(fromIFile(f).toASCIIString());
}
} else if (likelyGradle) {
IFile g = jp.getProject().getFile("build.gradle");
if (!g.exists()) {
g = jp.getProject().getFile("build.gradle.kts");
}
if (g.exists()) {
return ProjectBuild.createGradleBuild(g.getLocationURI().toASCIIString());
return ProjectBuild.createGradleBuild(fromIFile(g).toASCIIString());
}
}
// } catch (JavaModelException e) {
// // ignore
// }
return new ProjectBuild(null, null);
}

private static URI fromIFile(IFile f) {
return Paths.get(f.getLocationURI()).toUri();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.springframework.ide.vscode.boot.index.cache.IndexCache;
import org.springframework.ide.vscode.boot.index.cache.IndexCacheOnDiscDeltaBased;
import org.springframework.ide.vscode.boot.index.cache.IndexCacheVoid;
import org.springframework.ide.vscode.boot.java.BuildCommandProvider;
import org.springframework.ide.vscode.boot.java.JavaDefinitionHandler;
import org.springframework.ide.vscode.boot.java.beans.DependsOnDefinitionProvider;
import org.springframework.ide.vscode.boot.java.beans.NamedDefinitionProvider;
Expand Down Expand Up @@ -436,8 +437,8 @@ ModulithService modulithService(SimpleLanguageServer server, JavaProjectFinder p
}

@Bean
DataRepositoryAotMetadataService dataAotMetadataService() {
return new DataRepositoryAotMetadataService();
DataRepositoryAotMetadataService dataAotMetadataService(FileObserver fileObserver, JavaProjectFinder projectFinder, BuildCommandProvider buildCmds) {
return new DataRepositoryAotMetadataService(fileObserver, projectFinder, buildCmds);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.boot.java.BuildCommandProvider;
import org.springframework.ide.vscode.boot.java.DefaultBuildCommandProvider;
import org.springframework.ide.vscode.boot.java.VSCodeBuildCommandProvider;
import org.springframework.ide.vscode.boot.java.codeaction.JdtAstCodeActionProvider;
import org.springframework.ide.vscode.boot.java.codeaction.JdtCodeActionHandler;
import org.springframework.ide.vscode.boot.java.cron.CronExpressionsInlayHintsProvider;
Expand Down Expand Up @@ -228,4 +231,17 @@ public class JdtConfig {
@Bean JdtCodeActionHandler jdtCodeActionHandler(CompilationUnitCache cuCache, Collection<JdtAstCodeActionProvider> providers) {
return new JdtCodeActionHandler(cuCache, providers);
}

@Bean BuildCommandProvider buildCommandProvider(SimpleLanguageServer server) {
switch(LspClient.currentClient()) {
case VSCODE:
case THEIA:
return new VSCodeBuildCommandProvider();
case ECLIPSE:
return new VSCodeBuildCommandProvider();
default:
return new DefaultBuildCommandProvider(server);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ protected BootJavaCodeLensEngine createCodeLensEngine(SpringMetamodelIndex sprin
codeLensProvider.add(new WebfluxHandlerCodeLensProvider(springIndex));
codeLensProvider.add(new CopilotCodeLensProvider(projectFinder, server, spelSemanticTokens));
codeLensProvider.add(new RouterFunctionCodeLensProvider());
codeLensProvider.add(new DataRepositoryAotMetadataCodeLensProvider(projectFinder, repositoryAotMetadataService, refactorings, config));
codeLensProvider.add(new DataRepositoryAotMetadataCodeLensProvider(server, projectFinder, repositoryAotMetadataService, refactorings, config));
codeLensProvider.add(new WebConfigCodeLensProvider(projectFinder, springIndex, config));

return new BootJavaCodeLensEngine(this, codeLensProvider);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*******************************************************************************
* Copyright (c) 2025 Broadcom, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java;

import org.eclipse.lsp4j.Command;
import org.springframework.ide.vscode.commons.java.IJavaProject;

public interface BuildCommandProvider {

Command executeMavenGoal(IJavaProject project, String goal);

}
Loading