mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
Added jetbrains.api & jetbrains.api.impl modules
This commit is contained in:
committed by
Vitaly Provodin
parent
5f74c7d374
commit
d6569503d5
56
jb/project/tools/mkjbrapi.sh
Executable file
56
jb/project/tools/mkjbrapi.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
# How to call this script:
|
||||
# eval $(jb/project/tools/mkjbrapi.sh)
|
||||
|
||||
# It is used to build jetbrains.api module
|
||||
# After properly calling this script, you can use following variables:
|
||||
# JBR_API_JAR - absolute path to resulting JAR
|
||||
# JBR_API_SOURCES_JAR - absolute path to JAR with sources
|
||||
# JBR_API_VERSION - JBR API version in form <major>.<minor>
|
||||
# JBR_API_VERSION_MAJOR, JBR_API_VERSION_MINOR - JBR API version components
|
||||
# JBR_BUILD_DIR - absolute path to JBR build directory
|
||||
# JBR_BOOT_JDK - absolute path to used boot JDK
|
||||
|
||||
ROOT=$(pwd)
|
||||
|
||||
sh configure --with-debug-level=release --disable-warnings-as-errors 1>&2 || exit $?
|
||||
|
||||
# Get boot JDK & build directory using make script
|
||||
make -f $ROOT/make/JBRApi.gmk -I $ROOT jbr-api MAKEOVERRIDES= CONF=release OUT="$ROOT/build/jbr-api.cfg" 1>&2 || exit $?
|
||||
source "$ROOT/build/jbr-api.cfg" || exit $?
|
||||
|
||||
# Build module
|
||||
make jetbrains.api 1>&2 || exit $?
|
||||
|
||||
# Get JBR API version from compiled class
|
||||
JSHELL_COMMAND='
|
||||
System.out.println("VERSION_MAJOR=" + com.jetbrains.JBRApi.getMajorVersion());
|
||||
System.out.println("VERSION_MINOR=" + com.jetbrains.JBRApi.getMinorVersion());
|
||||
/exit'
|
||||
VERSION_VARIABLES=$("$BOOT_JDK/bin/jshell" -s --module-path "$BUILD_DIR/jdk/modules/jetbrains.api" \
|
||||
--add-modules jetbrains.api <<< "$JSHELL_COMMAND" | grep "^VERSION\|^|") || exit $?
|
||||
eval "$VERSION_VARIABLES" || exit $?
|
||||
|
||||
# Create JAR
|
||||
(
|
||||
cd "$BUILD_DIR/jdk/modules/jetbrains.api"
|
||||
"$BOOT_JDK/bin/jar" -cf "$BUILD_DIR/jbr-api.jar" * 1>&2
|
||||
) || exit $?
|
||||
|
||||
# Create source JAR
|
||||
(
|
||||
cd "src/jetbrains.api/share/classes"
|
||||
"$BOOT_JDK/bin/jar" -cf "$BUILD_DIR/jbr-api-sources.jar" * 1>&2
|
||||
) || exit $?
|
||||
|
||||
# Print output values
|
||||
echo "JBR_API_JAR=$BUILD_DIR/jbr-api.jar"
|
||||
echo "JBR_API_SOURCES_JAR=$BUILD_DIR/jbr-api-sources.jar"
|
||||
echo "JBR_API_VERSION=$VERSION_MAJOR.$VERSION_MINOR"
|
||||
echo "JBR_API_VERSION_MAJOR=$VERSION_MAJOR"
|
||||
echo "JBR_API_VERSION_MINOR=$VERSION_MINOR"
|
||||
echo "JBR_BUILD_DIR=$BUILD_DIR"
|
||||
echo "JBR_BOOT_JDK=$BOOT_JDK"
|
||||
|
||||
echo "Success!" 1>&2
|
||||
@@ -1,21 +1,21 @@
|
||||
diff --git modules.list modules.list
|
||||
index 7c4b3e9cb6d..5ed60349ca7 100644
|
||||
index 7896479c981..c1a5e664b86 100644
|
||||
--- modules.list
|
||||
+++ modules.list
|
||||
@@ -53,4 +53,7 @@ jdk.security.jgss,
|
||||
jdk.unsupported,
|
||||
jdk.xml.dom,
|
||||
@@ -50,4 +50,7 @@ jdk.xml.dom,
|
||||
jdk.zipfs,
|
||||
-jdk.hotspot.agent
|
||||
+jdk.hotspot.agent,
|
||||
jdk.hotspot.agent,
|
||||
jetbrains.api,
|
||||
-jetbrains.api.impl
|
||||
+jetbrains.api.impl,
|
||||
+jcef,
|
||||
+gluegen.rt,
|
||||
+jogl.all
|
||||
diff --git src/java.desktop/share/classes/module-info.java src/java.desktop/share/classes/module-info.java
|
||||
index b663b382f52..3e9acdc0c27 100644
|
||||
index 897647ee368..781d1809493 100644
|
||||
--- src/java.desktop/share/classes/module-info.java
|
||||
+++ src/java.desktop/share/classes/module-info.java
|
||||
@@ -109,7 +109,11 @@ module java.desktop {
|
||||
@@ -116,7 +116,11 @@ module java.desktop {
|
||||
// see make/GensrcModuleInfo.gmk
|
||||
exports sun.awt to
|
||||
jdk.accessibility,
|
||||
|
||||
20
make/JBRApi.gmk
Normal file
20
make/JBRApi.gmk
Normal file
@@ -0,0 +1,20 @@
|
||||
include Makefile
|
||||
include make/MainSupport.gmk
|
||||
|
||||
.PHONY: jbr-api
|
||||
|
||||
ifeq ($(SPEC),)
|
||||
ifneq ($(words $(SPECS)),1)
|
||||
@echo "Error: Multiple build specification files found. Please select one explicitly."
|
||||
@exit 2
|
||||
endif
|
||||
jbr-api:
|
||||
@cd $(topdir)
|
||||
@$(MAKE) $(MFLAGS) $(MAKE_LOG_FLAGS) -r -R -j 1 -f $(topdir)/make/JBRApi.gmk SPEC=$(SPECS) HAS_SPEC=true ACTUAL_TOPDIR=$(topdir) MODULES="$(MODULES)" jbr-api
|
||||
else #with SPEC
|
||||
|
||||
jbr-api:
|
||||
$(ECHO) "BUILD_DIR=$(OUTPUTDIR)" > $(OUT)
|
||||
$(ECHO) "BOOT_JDK=\"$(BOOT_JDK)\"" >> $(OUT)
|
||||
|
||||
endif
|
||||
@@ -55,6 +55,8 @@ BOOT_MODULES= \
|
||||
jdk.sctp \
|
||||
jdk.unsupported \
|
||||
jdk.naming.rmi \
|
||||
jetbrains.api \
|
||||
jetbrains.api.impl \
|
||||
#
|
||||
|
||||
# Modules that directly or indirectly requiring upgradeable modules
|
||||
|
||||
@@ -48,4 +48,6 @@ jdk.security.jgss,
|
||||
jdk.unsupported,
|
||||
jdk.xml.dom,
|
||||
jdk.zipfs,
|
||||
jdk.hotspot.agent
|
||||
jdk.hotspot.agent,
|
||||
jetbrains.api,
|
||||
jetbrains.api.impl
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.jetbrains.impl;
|
||||
|
||||
import com.jetbrains.SampleJBRApi;
|
||||
|
||||
// Here we just implement latest API version
|
||||
public class SampleJBRApiImpl implements SampleJBRApi.V2 {
|
||||
|
||||
@Override
|
||||
public void someMethod1(SomeDataClass arg) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SomeDataClass someMethod2() {
|
||||
return new SomeDataClass();
|
||||
}
|
||||
|
||||
}
|
||||
28
src/jetbrains.api.impl/share/classes/module-info.java
Normal file
28
src/jetbrains.api.impl/share/classes/module-info.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2000-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.jetbrains.JBRService;
|
||||
import com.jetbrains.impl.SampleJBRApiImpl;
|
||||
|
||||
module jetbrains.api.impl {
|
||||
|
||||
requires jetbrains.api;
|
||||
|
||||
// We probably don't want to add `provides with` for each version of API, so we just provide `JBRService`
|
||||
// implementation, `JBRService#load` will take care of the rest
|
||||
provides JBRService with SampleJBRApiImpl;
|
||||
|
||||
}
|
||||
50
src/jetbrains.api/share/classes/com/jetbrains/JBRApi.java
Normal file
50
src/jetbrains.api/share/classes/com/jetbrains/JBRApi.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2000-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.jetbrains;
|
||||
|
||||
public class JBRApi {
|
||||
|
||||
private static final int MAJOR_VERSION = 0;
|
||||
private static final int MINOR_VERSION = 0;
|
||||
|
||||
private JBRApi() {}
|
||||
|
||||
/**
|
||||
* Returns major API version.
|
||||
* It has nothing to do with {@link #isAvailable() actual implementation availability}.
|
||||
*/
|
||||
public static int getMajorVersion() {
|
||||
return MAJOR_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns minor API version.
|
||||
* It has nothing to do with {@link #isAvailable() actual implementation availability}.
|
||||
*/
|
||||
public static int getMinorVersion() {
|
||||
return MINOR_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for JBR API implementation availability at runtime.
|
||||
* {@code true} means we're running on JBR with {@code jetbrains.api.impl} module.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return JBRService.isApiAvailable();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2000-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.jetbrains;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Marker interface, can be used with Java SPI to enumerate JBR services.
|
||||
* All JBR services must implement this interface
|
||||
*/
|
||||
public interface JBRService {
|
||||
|
||||
|
||||
/**
|
||||
* Tries to load JBR service for given interface.
|
||||
* Returns null when there's no implementation for such interface, or when given Supplier throws
|
||||
* NoClassDefFoundError, which usually means that given interface is absent in module path.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends JBRService> T load(Supplier<Class<T>> serviceInterface) {
|
||||
try {
|
||||
JBRServiceManager.ServiceHolder<T> serviceHolder =
|
||||
(JBRServiceManager.ServiceHolder<T>) JBRServiceManager.services.get(serviceInterface.get());
|
||||
return serviceHolder == null ? null : serviceHolder.get();
|
||||
} catch (NoClassDefFoundError ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isApiAvailable() {
|
||||
return JBRServiceManager.services.size() > 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class JBRServiceManager {
|
||||
|
||||
static class ServiceHolder<T extends JBRService> {
|
||||
private ServiceLoader.Provider<T> provider;
|
||||
private T service;
|
||||
private ServiceHolder(ServiceLoader.Provider<T> provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
synchronized T get() {
|
||||
if(service != null) return service;
|
||||
service = provider.get();
|
||||
provider = null;
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
static final Map<Class<? extends JBRService>, ServiceHolder<?>> services;
|
||||
static {
|
||||
Map<Class<? extends JBRService>, ServiceHolder<?>> map = new HashMap<>();
|
||||
ServiceLoader.load(JBRService.class).stream()
|
||||
.forEach(p -> populateServiceMap(map, new ServiceHolder<>(p), p.type()));
|
||||
services = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all chains of class/interface hierarchy that leads from given {@code clazz} to {@link JBRService}
|
||||
* and assigns given {@code serviceHolder} to them
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static boolean populateServiceMap(Map<Class<? extends JBRService>, ServiceHolder<?>> map,
|
||||
ServiceHolder<?> serviceHolder, Class<?> clazz) {
|
||||
if(clazz == null) return false;
|
||||
if(clazz.equals(JBRService.class)) return true;
|
||||
boolean isSubtypeOfJBRService = false;
|
||||
for(Class<?> ifc : clazz.getInterfaces()) {
|
||||
isSubtypeOfJBRService |= populateServiceMap(map, serviceHolder, ifc);
|
||||
}
|
||||
isSubtypeOfJBRService |= populateServiceMap(map, serviceHolder, clazz.getSuperclass());
|
||||
if(isSubtypeOfJBRService) {
|
||||
map.put((Class<? extends JBRService>) clazz, serviceHolder);
|
||||
}
|
||||
return isSubtypeOfJBRService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.jetbrains;
|
||||
|
||||
// SampleJBRApi is a base of our feature API, it's just a marker interface extending JBRService
|
||||
public interface SampleJBRApi extends JBRService {
|
||||
|
||||
|
||||
// And actual API goes from V1
|
||||
interface V1 extends SampleJBRApi {
|
||||
|
||||
void someMethod1(SomeDataClass arg);
|
||||
|
||||
class SomeDataClass {
|
||||
public SomeDataClass() {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// When we need to add something to our API, we create interface for a new version, extending previous one
|
||||
interface V2 extends V1 {
|
||||
|
||||
SomeDataClass someMethod2();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
25
src/jetbrains.api/share/classes/module-info.java
Normal file
25
src/jetbrains.api/share/classes/module-info.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2000-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.jetbrains.JBRService;
|
||||
|
||||
module jetbrains.api {
|
||||
|
||||
exports com.jetbrains;
|
||||
|
||||
uses JBRService;
|
||||
|
||||
}
|
||||
77
test/jdk/jb/java/api/JBRServiceTest.java
Normal file
77
test/jdk/jb/java/api/JBRServiceTest.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2000-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.jetbrains.JBRService;
|
||||
import com.jetbrains.SampleJBRApi;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary JBR API service loading & usage example
|
||||
*/
|
||||
|
||||
public class JBRServiceTest {
|
||||
|
||||
|
||||
/**
|
||||
* All API members are organized into some kind of 'versioned namespaces', here's the example:
|
||||
* <ul>
|
||||
* <li>SampleJBRApi - root of our feature API.</li>
|
||||
* <li>SampleJBRApi.V1 - first version of our API. It contains all relevant methods, classes, etc.</li>
|
||||
* <li>SampleJBRApi.V1.SomeDataClass - data class, added by first version of API.</li>
|
||||
* <li>SampleJBRApi.V2 - second version of our API. It extends V1, adding some more things to API.</li>
|
||||
* </ul>
|
||||
*
|
||||
* Actual interface hierarchy for our API looks like this:
|
||||
* <ul>
|
||||
* <li>JBRService: marker interface, root for every feature API</li>
|
||||
* <ul>
|
||||
* <li>SampleJBRApi: marker interface, root for that specific feature API</li>
|
||||
* <ul>
|
||||
* <li>SampleJBRApi.V1: first version of feature API</li>
|
||||
* <ul>
|
||||
* <li>SampleJBRApi.V2: second version of feature API</li>
|
||||
* </ul>
|
||||
* </ul>
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* General rule when using JBR API is to avoid loading classes unless you make sure they're available at runtime.
|
||||
* To check for service availability you can try loading it with {@link JBRService#load}.
|
||||
* Also avoid using 'instanceof' to test JBR service implementation against specific interface, because using
|
||||
* it in 'instanceof' statement will cause loading this interface, which may in turn cause NoClassDefFoundError.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// Declaring variables is safe
|
||||
SampleJBRApi.V1 service;
|
||||
// We pass lambda instead of plain class. This allows JBRService#load to deal with NoClassDefFoundErrors
|
||||
service = JBRService.load(() -> SampleJBRApi.V1.class);
|
||||
// Null-checking variables is safe too
|
||||
Objects.requireNonNull(service);
|
||||
// When we ensured that SampleJBRApi.V1 is available, we can use anything that's inside
|
||||
service.someMethod1(null);
|
||||
// We can also create SampleJBRApi.V1.SomeDataClass instances, as we know they're supported with V1
|
||||
service.someMethod1(new SampleJBRApi.V1.SomeDataClass());
|
||||
|
||||
// But don't try doing 'service instanceof SampleJBRApi.V2' as it may cause NoClassDefFoundErrors!
|
||||
SampleJBRApi.V2 service2 = Objects.requireNonNull(JBRService.load(() -> SampleJBRApi.V2.class));
|
||||
// Versioned service interfaces are inherited, so V2 gives you access to both V2 and V1 API
|
||||
service2.someMethod1(service2.someMethod2());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user