Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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,46 @@
/*******************************************************************************
* Copyright (c) 2026 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/

package io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.codeAction.proposal;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiModifierListOwner;

import java.util.List;

/**
* Code action proposal for removing methods from a class.
*/
public class RemoveMethodProposal extends RemoveElementsProposal {

/**
* Constructor for RemoveMethodProposal.
*
* @param label The label for this proposal
* @param sourceCU The source compilation unit
* @param invocationNode The invocation node
* @param binding The modifier list owner (the class)
* @param relevance The relevance score
* @param methodsToRemove List of methods to remove
*/
public RemoveMethodProposal(
String label,
PsiFile sourceCU,
PsiFile invocationNode,
PsiModifierListOwner binding,
int relevance,
List<? extends PsiElement> methodsToRemove) {
super(label, sourceCU, invocationNode, binding, relevance, methodsToRemove);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ public class EjbConstants {

public static final String DIAGNOSTIC_SOURCE = "jakarta-ejb";
public static final String DIAGNOSTIC_CODE_MISSING_PUBLIC_CONSTRUCTOR = "MissingPublicNoArgConstructor";
public static final String DIAGNOSTIC_CODE_FINALIZE_METHOD = "SessionBeanFinalizeMethod";

public static final String FINALIZE_METHOD_NAME = "finalize";
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public void collectDiagnostics(PsiJavaFile unit, List<Diagnostic> diagnostics) {

if (!sessionBeanAnnotations.isEmpty()) {
validateSessionBeanConstructor(type, unit, diagnostics);
validateSessionBeanFinalizeMethod(type, unit, diagnostics);
}
}
}
Expand Down Expand Up @@ -98,4 +99,31 @@ private void validateSessionBeanConstructor(PsiClass type, PsiJavaFile unit, Lis
DiagnosticSeverity.Error));
}
}

/**
* Validates that a session bean does not define or override the finalize() method.
*
* According to the Jakarta EE Enterprise Beans specification, session bean classes
* must not override or define the finalize() method. The container manages the
* lifecycle and cleanup of session beans.
*
* @param type the class to validate
* @param unit the compilation unit
* @param diagnostics the list to add diagnostics to
*/
private void validateSessionBeanFinalizeMethod(PsiClass type, PsiJavaFile unit, List<Diagnostic> diagnostics) {
// Check all methods in the class
for (PsiMethod method : type.getMethods()) {
// Check if this is the finalize() method
if (FINALIZE_METHOD_NAME.equals(method.getName()) &&
method.getParameterList().getParametersCount() == 0) {
Comment thread
venmanyarun marked this conversation as resolved.
// Report diagnostic for finalize() method
diagnostics.add(createDiagnostic(method, unit,
Messages.getMessage("SessionBeanFinalizeMethod"),
DIAGNOSTIC_CODE_FINALIZE_METHOD,
null,
DiagnosticSeverity.Error));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*******************************************************************************
* Copyright (c) 2026 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/

package io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.ejb;

import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.JDTUtils;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.Messages;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.codeAction.proposal.RemoveMethodProposal;

import static io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.ejb.EjbConstants.FINALIZE_METHOD_NAME;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.IJavaCodeActionParticipant;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.JavaCodeActionContext;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.JavaCodeActionResolveContext;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.corrections.proposal.ChangeCorrectionProposal;
import io.openliberty.tools.intellij.util.ExceptionUtil;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.Diagnostic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

/**
* QuickFix for removing the finalize() method from session beans.
*/
public class RemoveFinalizeMethodQuickFix implements IJavaCodeActionParticipant {

private static final String NAME = Messages.getMessage("RemoveFinalizeMethod");
private static final Logger LOGGER = Logger.getLogger(RemoveFinalizeMethodQuickFix.class.getName());

@Override
public String getParticipantId() {
return RemoveFinalizeMethodQuickFix.class.getName();
}

@Override
public List<? extends CodeAction> getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic) {
List<CodeAction> codeActions = new ArrayList<>();
final PsiElement node = context.getCoveredNode();
final PsiMethod parentMethod = PsiTreeUtil.getParentOfType(node, PsiMethod.class);

if (parentMethod != null && FINALIZE_METHOD_NAME.equals(parentMethod.getName()) &&
parentMethod.getParameterList().getParametersCount() == 0) {
codeActions.add(JDTUtils.createCodeAction(context, diagnostic, NAME, getParticipantId()));
}
return codeActions;
}

@Override
public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) {
final CodeAction toResolve = context.getUnresolved();
final PsiElement node = context.getCoveredNode();
final PsiClass parentType = PsiTreeUtil.getParentOfType(node, PsiClass.class);
final PsiMethod parentMethod = PsiTreeUtil.getParentOfType(node, PsiMethod.class);

if (parentMethod != null && FINALIZE_METHOD_NAME.equals(parentMethod.getName())) {
ChangeCorrectionProposal proposal = new RemoveMethodProposal(NAME,
context.getSource().getCompilationUnit(),
context.getASTRoot(),
parentType,
0,
Collections.singletonList(parentMethod));

ExceptionUtil.executeWithWorkspaceEditHandling(context, proposal, toResolve, LOGGER,
"Unable to create workspace edit for removing finalize() method");
}
return toResolve;
}
}
4 changes: 4 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,10 @@
group="jakarta"
targetDiagnostic="jakarta-ejb#MissingPublicNoArgConstructor"
implementationClass="io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.ejb.InsertPublicNoArgConstructorQuickFix"/>
<javaCodeActionParticipant kind="quickfix"
group="jakarta"
targetDiagnostic="jakarta-ejb#SessionBeanFinalizeMethod"
implementationClass="io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.ejb.RemoveFinalizeMethodQuickFix"/>

<configSourceProvider
implementation="io.openliberty.tools.intellij.lsp4mp4ij.psi.internal.core.providers.MicroProfileConfigSourceProvider"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ InvalidInterceptorMethodAnnotationStaticMethod = {0} interceptor method must not

# EjbDiagnosticsCollector
SessionBeanNoArgConstructor = Session beans must have a public no-arg constructor.
SessionBeanFinalizeMethod = Session bean classes must not override or define the finalize() method.

# InsertPublicNoArgConstructorQuickFix
AddPublicNoArgConstructor = Add a no-arg public constructor to this class
AddPublicNoArgConstructor = Add a no-arg public constructor to this class

# RemoveFinalizeMethodQuickFix
RemoveFinalizeMethod = Remove the finalize() method
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*******************************************************************************
* Copyright (c) 2026 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/

package io.openliberty.tools.intellij.lsp4jakarta.it.ejb;

import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import io.openliberty.tools.intellij.lsp4jakarta.it.core.BaseJakartaTest;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams;
import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.io.File;
import java.util.Arrays;

import static io.openliberty.tools.intellij.lsp4jakarta.it.core.JakartaForJavaAssert.*;

@RunWith(JUnit4.class)
public class SessionBeanFinalizeTest extends BaseJakartaTest {

@Test
public void testInvalidStatelessBeanFinalize() throws Exception {
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());

VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
+ "/src/main/java/io/openliberty/sample/jakarta/ejb/InvalidStatelessBeanFinalize.java");
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();

JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
diagnosticsParams.setUris(Arrays.asList(uri));

// Test expected diagnostic
Diagnostic expectedDiagnostic = d(18, 19, 27,
"Session bean classes must not override or define the finalize() method.",
DiagnosticSeverity.Error, "jakarta-ejb", "SessionBeanFinalizeMethod");

assertJavaDiagnostics(diagnosticsParams, utils, expectedDiagnostic);

// Test expected quick-fix
JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, expectedDiagnostic);
String newText = "package io.openliberty.sample.jakarta.ejb;" +
"\n\nimport jakarta.ejb.Stateless;" +
"\n\n@Stateless" +
"\npublic class InvalidStatelessBeanFinalize {" +
"\n private String data;" +
"\n\n public String getData() {" +
"\n return data;" +
"\n }" +
"\n\n public void setData(String data) {" +
"\n this.data = data;" +
"\n }" +
"\n\n}";
TextEdit expectedTextEdit = te(0, 0, 22, 1, newText);
CodeAction expectedCodeAction = ca(uri, "Remove the finalize() method", expectedDiagnostic, expectedTextEdit);
assertJavaCodeAction(codeActionParams, utils, expectedCodeAction);
}

@Test
public void testInvalidStatefulBeanFinalize() throws Exception {
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());

VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
+ "/src/main/java/io/openliberty/sample/jakarta/ejb/InvalidStatefulBeanFinalize.java");
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();

JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
diagnosticsParams.setUris(Arrays.asList(uri));

// Test expected diagnostic
Diagnostic expectedDiagnostic = d(18, 19, 27,
"Session bean classes must not override or define the finalize() method.",
DiagnosticSeverity.Error, "jakarta-ejb", "SessionBeanFinalizeMethod");

assertJavaDiagnostics(diagnosticsParams, utils, expectedDiagnostic);

// Test expected quick-fix
JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, expectedDiagnostic);
String newText = "package io.openliberty.sample.jakarta.ejb;" +
"\n\nimport jakarta.ejb.Stateful;" +
"\n\n@Stateful" +
"\npublic class InvalidStatefulBeanFinalize {" +
"\n private int count;" +
"\n\n public int getCount() {" +
"\n return count;" +
"\n }" +
"\n\n public void setCount(int count) {" +
"\n this.count = count;" +
"\n }" +
"\n\n}";
TextEdit expectedTextEdit = te(0, 0, 22, 1, newText);
CodeAction expectedCodeAction = ca(uri, "Remove the finalize() method", expectedDiagnostic, expectedTextEdit);
assertJavaCodeAction(codeActionParams, utils, expectedCodeAction);
}

@Test
public void testInvalidSingletonBeanFinalize() throws Exception {
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());

VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
+ "/src/main/java/io/openliberty/sample/jakarta/ejb/InvalidSingletonBeanFinalize.java");
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();

JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
diagnosticsParams.setUris(Arrays.asList(uri));

// Test expected diagnostic
Diagnostic expectedDiagnostic = d(18, 19, 27,
"Session bean classes must not override or define the finalize() method.",
DiagnosticSeverity.Error, "jakarta-ejb", "SessionBeanFinalizeMethod");

assertJavaDiagnostics(diagnosticsParams, utils, expectedDiagnostic);

// Test expected quick-fix
JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, expectedDiagnostic);
String newText = "package io.openliberty.sample.jakarta.ejb;" +
"\n\nimport jakarta.ejb.Singleton;" +
"\n\n@Singleton" +
"\npublic class InvalidSingletonBeanFinalize {" +
"\n private String config;" +
"\n\n public String getConfig() {" +
"\n return config;" +
"\n }" +
"\n\n public void setConfig(String config) {" +
"\n this.config = config;" +
"\n }" +
"\n\n}";
TextEdit expectedTextEdit = te(0, 0, 22, 1, newText);
CodeAction expectedCodeAction = ca(uri, "Remove the finalize() method", expectedDiagnostic, expectedTextEdit);
assertJavaCodeAction(codeActionParams, utils, expectedCodeAction);
}

@Test
public void testValidStatelessBeanNoFinalize() throws Exception {
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());

VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
+ "/src/main/java/io/openliberty/sample/jakarta/ejb/ValidStatelessBeanNoFinalize.java");
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();

JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams();
diagnosticsParams.setUris(Arrays.asList(uri));

// Should not report any diagnostics for valid bean without finalize()
assertJavaDiagnostics(diagnosticsParams, utils);
}
}
Loading
Loading