Added jetbrains.api & jetbrains.api.impl modules

This commit is contained in:
Nikita Gubarkov
2020-09-12 06:04:59 +03:00
committed by Vitaly Provodin
parent 5f74c7d374
commit d6569503d5
12 changed files with 413 additions and 9 deletions

56
jb/project/tools/mkjbrapi.sh Executable file
View 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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}
}

View 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;
}

View 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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View 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;
}

View 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());
}
}