-
Notifications
You must be signed in to change notification settings - Fork 208
Developer Manual Java Messages
Spring Boot LS requires quite a bit of "Java knowledge" such as search for type given fully qualified name, access getters/setters on a java type, javadoc for various Java artifacts etc. At first support for "Java knowledge" has been implemented on STS LS side with the following projects in commons
:
-
commons-java
(jandex part of it) commons-maven
commons-gradle
Having the support for Java indexing, search etc in the Spring Boot LS isn't ideal since this is the duplication of a Java LS or Eclipse JDT features. Therefore, it'd be nice to get all java data either straight from JDT LS or JDT in the Eclipse client case.
Once communication with JDT LS via the client has been implemented (See Communication with JDT LS) it became possible to extend the LSP protocol with messages aimed to retrieve necessary Java data for the Spring Boot LS. There are two implementation for retrieving "Java Knowledge" that coexist in the project at the moment:
- JDT/JDT LS communication via LSP extension
- Legacy in-house developed implementation with Jandex indexing (will be removed in the future)
The Spring Boot LS decides which implementation to use at runtime based on the success of sts/addClasspathListsner
message (See sts/addClasspathListener). If the request comes back with an error the Legacy implementation is used, otherwise the new LSP extension implementation used.
Start with implementing handlers for sts/addClasspathListener
and sts/removeClasspathListener
request messages on the client. These messages are the central point as they allow LS to track Java projects with their classpath and decide which of them are Spring Boot projects with which the Spring Boot LS tooling should be working. Debug ClasspathListenerManager
on the LS side to ensure classpath listener is working by verifying that classpath changes are received on the LS at proper times and handled appropriately (ClasspathListenerManager#addClasspathListener(...)
). Set languageserver.boot.enable-jandex-index=true
Boot property. Thus, Jandex index would be used to index Java projects making Java search still work regardless of the following LSP extension messages not yet implemented:
sts/javaType
sts/javaSearchTypes
sts/javaSearchPackages
sts/javaSubTypes
sts/javaSuperTypes
The above messages should be implemented the last
Next LSP extension Java messages to implement would be:
sts/javadoc
- used in Boot property hovers where property key corresponds to a Java setter or field.
The JdtLsJavadocProvider
is hooked to this request message. This provider starts being used once sts/addClasspathListener
and sts/removeClasspathListener
are implemented and functional.
sts/javaLocation
- used for hyper links in Boot property files (Ctrl-Click)
See org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider
interface on the Spring Boot LS side. Once the request message is implemented on the client ensure JavaServerElementLocationProvider
for the client type is created in BootLanguagServerBootApp
class.
sts/javadocHoverLink
- used in Live bean hovers to navigate to bean definition or type.
See org.springframework.ide.vscode.boot.java.links.SourceLinks
interface on the Spring Boot LS side. Once the request message is implemented ensure JavaServerSourceLinks
for the client type is created in SourceLinkFactory
class.
Finally, implement the Java search request messages:
sts/javaType
sts/javaSearchTypes
sts/javaSearchPackages
sts/javaSubTypes
sts/javaSuperTypes
These may need to be fine tuned for performance as Java searches are time consuming. Be prepared to cache the first N results for wide searches. When implementing these make sure Boot property languageserver.boot.enable-jandex-index
is switched to false
or removed from LS JAR launch arguments.
Request to register a classpath listener with special unique string
id on the client. The client then sends notifications to the LS that registered the listener in a form of LSP workspace/executeCommand
request sent from the client (See workspace/ExecuteCommand, with LSPs ExecuteCommandParams
data, where:
-
id
property is the registered listener id -
arguments
is the list of classpath data for a project defined as follows:- Project URI
string
- Project name
string
- Is deleted
boolean
- The classpath of type
Classpath
defined below
- Project URI
interface Classpath {
entries: CPE[]
}
(See CPE for details on classpath entry data format) Important. As soon as the listener is registered the client is expected to send the data about project classpaths and then when that classpath changes or project becomes deleted. The classpath listsner also serves as project tracking tool to let STS LS know when new projects are created, old deleted and classpath of exisitng project changes.
- method:
sts/addClasspathListener
- params:
ClasspathListenerParams
defined below
interface ClasspathListenerParams {
callbackCommandId: string; // id of the listener and command that client will execute on the LS that registered the listener. This unique id is generated on the LS before registering the listener
}
- result
{}
, error if failed to register listener
If exception/error response is received on the LS then Spring Boot LS will start using the fallback mechanism to find Spring Boot Java Maven and Gradle projects and index their content with Jandex index. This is not ideal since it'd duplicate the work Java LS or JDT is doing (and probably doing it better than us)
Request to remove the classpath listener from the client
- method:
sts/removeClasspathListener
- params:
ClasspathListenerParams
defined below
interface ClasspathListenerParams {
callbackCommandId: string; // id of the listener
}
- result
{}
, error if failed
Request sent to the client from the STS LS to get javadoc content in Markdown format for a Java artifact (type, method, field, etc)
- method:
sts/javadoc
- params:
JavaDataParams
(See JavaDataParams)
- result:
MarkupContent
defined by LSP
Requests the client to send the Java type detailed data to STS LS.
- method:
sts/javaType
- params:
JavaDataParams
(See JavaDataParams)
- result:
TypeData
defined below
interface TypeData {
name: string; // name of the type not fully qualified
handleIdentifier: string; // JDT identifier handle (may not be necessary, currently unused)
label: string; // Type's label to display in the UI
declaringType: string; // Declaring type (in case of the current type being a nested type) binding key
flags: number; // Java flags (access data such private/public/protected would be here, abstract or concrete etc)
fqName: string; // Fully qualified name of the type
clazz: boolean; // Is type a class?
annotation: boolean; // Is type an annotation?
interfaze: boolean; // Is type an interface?
enam: boolean; // Is type an enum?
superClassName: string; // Name of the superclass (non fully qualified) or undefined if there isn't any (currently unused)
superInterfaceNames: string[]; // Names of the super intefaces (non fully qualified) or undefined if there isn't any (currently unused)
bindingKey: string; // Java type binding key, i.e. Ljava/util/List;
fields: FieldData[]; // Type fields data
methods: MethodData[]; // Type methods data
annotations: AnnotationData[]; // Type annotations data
classpathEntry: ClasspathEntryData; // Type's classpath entry data
}
(See FieldData, MethodData, AnnotationData, ClasspathEntryData for more details)
Requests the client the Java artifact link text suitable for showing in the client's hover control. The markdown hyperlink link part. It is the jdt
URL in VSCode and Theia. The special javadoc hover URL in Eclipse. Clicking on such link in the hover control should navigate the user to the Java artifact definition.
- method:
sts/javadocHoverLink
- params:
JavaDataParams
(See JavaDataParams)
- result:
string
- the hyperlink URI/URL
Requests the client the LSP Location
of the definition of a Java artifact given by its binding key
- method:
sts/javaLocation
- params:
JavaDataParams
(See JavaDataParams)
- result: LSP
Location
- Document and position in the document of the Java artifact definition in the source code
Requests the client the search results for java type given a search query with JavaSearchParams
- method:
sts/javaSearchTypes
- params:
JavaSearchParams
(See JavaSearchParams)
- result:
TypeDescriptorData[]
(See TypeDescriptorData)
Requests the client the search results for java package given a search query with JavaSearchParams
- method:
sts/javaSearchPackages
- params:
JavaSearchParams
(See JavaSearchParams)
- result:
string[]
package names
Requests the client all sub types of a given Java type
- method:
sts/javaSubTypes
- params:
JavaTypeHierarchyParams
(See JavaTypeHierarchyParams)
- result:
TypeDescriptorData[]
(See TypeDescriptorData) All subtypes data
Requests the client all super types of a given Java type
- method:
sts/javaSuperTypes
- params:
JavaTypeHierarchyParams
(See JavaTypeHierarchyParams)
- result:
TypeDescriptorData[]
(See TypeDescriptorData) All super types data
Data types used across STS4 LSP extensions messages.
interface JavaDataParams {
projectUri: string; // Java project URI
bindingKey: string; // Java artifact binding key which is "binary name" of the artifact, e.g. for java.util.List it is Ljava/util/List;
lookInOtherProjects: boolean; // If type cannot be found in the project specified by the URI look in the whole workspace
}
Java field information sent from the client to STS LS
interface FieldData {
name: string; // name of the type not fully qualified
handleIdentifier: string; // JDT identifier handle (may not be necessary, currently unused)
label: string; // Type's label to display in the UI
declaringType: string; // Declaring type binding key
flags: number; // Java flags (access data such private/public/protected would be here, abstract or concrete etc)
bindingKey: string; // Java field binding key, i.e. Ljava/util/AbstractList;.modCount)I
type: JavaTypeData; // Field type data
enumConstant: boolean; // Is field enum constant?
annotations: AnnotationData[]; // Field's annotations data
}
Java method data sent from the client to STS LS
interface MethodData {
name: string; // name of the type not fully qualified
handleIdentifier: string; // JDT identifier handle (may not be necessary, currently unused)
label: string; // Type's label to display in the UI
declaringType: string; // Declaring type binding key
flags: number; // Java flags (access data such private/public/protected would be here, abstract or concrete etc)
bindingKey: string; // Java method binding key, i.e. Ljava/util/Map;.size()I
constructor: boolean; // Is constructor method?
returnType: JavaTypeData; // Method return type
parameters: JavaTypeData[]; // parameter types (and order)
annotations: AnnotationData[]; // method annotations data
}
Java annotation data sent from the client to STS LS
interface AnnotationData {
name: string; // name of the type not fully qualified
handleIdentifier: string; // JDT identifier handle (may not be necessary, currently unused)
label: string; // Type's label to display in the UI
fqName: string; // Annotations' fully qualified name
valuePairs: {}; // Annotation's key value pairs string -> string
}
Java Classpath entry data sent from the client to STS LS
interface ClasspathEntryData {
module: string; // Java module name, i.e. java.base
entry: CPE; // Classpath entry
}
Single classpath entry data. Either JAR file or a folder.
interface CPE {
kind: 'source' | 'binary'; // Entry kind: source or binary
path: string; // Entry absolute file path (might be changed to URL/URI)
outputFolder: string; // Entry output folder for the case of the source entry
sourceContainerUrl: string; // Source container URL. Either JAR url or source folder
javadocContainerUrl: string; // Javadoc container URL. Either javadoc JAR or URL of the javadoc site (may not be useful)
isSystem: boolean; // Is entry a system entry, i.e. a JRE/JDK jar
isOwn: boolean; // Does classpath entry belong to the project? Might be a folder from another project in the workspace
isTest: boolean; // Is classpath entry for test scope only (Maven, Gradle test scope)
isJavaContent; // Classpath entry is a source folder. If it has Java files then flag is `true`. If it's resources folder without packages then flag is `false`
}
interface JavaTypeData {
kind: JavaTypeKind; // Kind of java type: class, interface, primitive, etc.
name: string; // Type name ('B', 'I', 'Z', etc. for primitive types)
extras: {}; // Extra data for the type in key-value form
}
The extras
map is used for keeping detailed data for complex types such as parameterized types, arrays, etc.
Arrays expected to have:
-
"dimensions"
key with integer value -
"component"
key withJavaTypeData
value of the erasure type Parameterized types expected to have: -
"owner"
key withJavaTypeData
value of the erasure type -
"arguments"
key withJavaTypeData[]
value for argument types
enum JavaTypeKind {
CLASS,
ARRAY,
PARAMETERIZED,
TYPE_VARIABLE,
WILDCARD,
VOID,
INT,
CHAR,
BOOLEAN,
FLOAT,
BYTE,
DOUBLE,
LONG,
SHORT,
UNRESOLVED
}
Parameter specifying a search query for Java artifact search
interface JavaSearchParams {
projectUri: string; // Project URI
term: string; // search term, "MyCl" (typically this entered by user)
includeBinaries: boolean; // Are JARs and .class files included in the search, i.e. dependency libs
includeSystemLibs: boolean; // Are JRE/JDK libs included in the search?
timeLimit: number; // Time limit on the search. Negative number means no time limit on the search
searchType: 'fuzzy'|'camelcase'; // Search "MyCl": Fuzzy -> "*m*y*C*l*", Camel-Case -> "My*Cl*"
}
The shorter version of TypeData
from sts/javaType response. Shorter to make the response more performant without resolving field types, method return and argument types.
interface TypeDescriptorData {
name: string; // name of the type not fully qualified
handleIdentifier: string; // JDT identifier handle (may not be necessary, currently unused)
label: string; // Type's label to display in the UI
declaringType: string; // Declaring type (in case of the current type being a nested type) binding key
flags: number; // Java flags (access data such private/public/protected would be here, abstract or concrete etc)
fqName: string; // Fully qualified name of the type
clazz: boolean; // Is type a class?
annotation: boolean; // Is type an annotation?
interfaze: boolean; // Is type an interface?
enam: boolean; // Is type an enum?
superClassName: string; // Name of the superclass (non fully qualified) or undefined if there isn't any (currently unused)
superInterfaceNames: string[]; // Names of the super intefaces (non fully qualified) or undefined if there isn't any (currently unused)
}
interface JavaTypeHierarchyParams {
projectUri: string; // project URI
fqName: string; // Fully qualified name of a Java type
includeFocusType: boolean; // Include the current type in the the hierarchy response
}
- Installation (latest release + snapshots)
- User Guide
- Getting Started
- Navigation
- Live Application Information
- Content Assist
- Version Validation
- Upgrade Support
- Validations and Quick Fixes
- WebFlux Support
- Boot Properties Editor
- Boot Dashboard
- Other Editors
- STS3
- Custom VM args
- FAQ
- Changelog
- Known Limitations & Issues
- Report an Issue
- Developer Manual
- Overview
- Language Server Integration into Clients
- Communication with JDT LS
- STS4 Language Server Protocol Extensions