Skip to content

Developer Manual Java Messages

Martin Lippert edited this page Jan 10, 2021 · 2 revisions

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.

Switching to LSP Extension Implementation

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.

sts/addClasspathListener

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:
    1. Project URI string
    2. Project name string
    3. Is deleted boolean
    4. The classpath of type Classpath defined below
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.

Request

  • 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
}

Response

  • result {}, error if failed to register listener

Important Notes

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)

sts/removeClasspathListener

Request to remove the classpath listener from the client

Request

  • method: sts/removeClasspathListener
  • params: ClasspathListenerParams defined below
interface ClasspathListenerParams {
  callbackCommandId: string; // id of the listener
}

Response

  • result {}, error if failed

sts/javadoc

Request sent to the client from the STS LS to get javadoc content in Markdown format for a Java artifact (type, method, field, etc)

Request

Response

  • result: MarkupContent defined by LSP

sts/javaType

Requests the client to send the Java type detailed data to STS LS.

Request

Response

  • 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)

sts/javadocHoverLink

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.

Request

  • method: sts/javadocHoverLink
  • params: JavaDataParams (See JavaDataParams)

Response

  • result: string - the hyperlink URI/URL

sts/javaLocation

Requests the client the LSP Location of the definition of a Java artifact given by its binding key

Request

  • method: sts/javaLocation
  • params: JavaDataParams (See JavaDataParams)

Response

  • result: LSP Location - Document and position in the document of the Java artifact definition in the source code

sts/javaSearchTypes

Requests the client the search results for java type given a search query with JavaSearchParams

Request

Response

sts/javaSearchPackages

Requests the client the search results for java package given a search query with JavaSearchParams

Request

  • method: sts/javaSearchPackages
  • params: JavaSearchParams (See JavaSearchParams)

Response

  • result: string[] package names

sts/javaSubTypes

Requests the client all sub types of a given Java type

Request

Response

sts/javaSuperTypes

Requests the client all super types of a given Java type

Request

Response

Data Types

Data types used across STS4 LSP extensions messages.

JavaDataParams

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
}

FieldData

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
}

MethodData

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
}

AnnotationData

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
}

ClasspathEntryData

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
}

CPE

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`
}

JavaTypeData

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 with JavaTypeData value of the erasure type Parameterized types expected to have:
  • "owner" key with JavaTypeData value of the erasure type
  • "arguments" key with JavaTypeData[] value for argument types

JavaTypeKind

enum JavaTypeKind {
  CLASS,
  ARRAY,
  PARAMETERIZED,
  TYPE_VARIABLE,
  WILDCARD,
  VOID,
  INT,
  CHAR,
  BOOLEAN,
  FLOAT,
  BYTE,
  DOUBLE,
  LONG,
  SHORT,
  UNRESOLVED
}

JavaSearchParams

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*"
}

TypeDescriptorData

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)
}

JavaTypeHierarchyParams

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
}
Clone this wiki locally