tools/templates/JBR.java (99 lines of code) (raw):

/* * Copyright 2000-2024 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.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.EnumMap; import java.util.Map; import java.util.function.Function; /** * Entry point into JBR API. * Client and JBR side are linked dynamically at runtime and do not have to be of the same version. * In some cases (e.g. running on different JRE or old JBR) system will not be able to find * implementation for some services, so you'll need a fallback behavior for that case. * <h2>Simple usage example:</h2> * <blockquote><pre>{@code * if (JBR.isSomeServiceSupported()) { * JBR.getSomeService().doSomething(); * } else { * planB(); * } * }</pre></blockquote> * <h3>Implementation note:</h3> * JBR API is initialized on first access to this class (in static initializer). * Actual implementation is linked on demand, when corresponding service is requested by client. */ public final class JBR { private static final ServiceApi api; private static final Throwable bootstrapException; static { ServiceApi a = null; Throwable exception = null; try { MethodHandles.Lookup lookup = MethodHandles.lookup(); try { // New version of bootstrap method Class<?> bootstrap = Class.forName("com.jetbrains.exported.JBRApiSupport"); a = (ServiceApi) (Object) lookup .findStatic(bootstrap, "bootstrap", MethodType.methodType(Object.class, Class.class, Class.class, Class.class, Class.class, Map.class, Function.class)) .invokeExact(ServiceApi.class, Service.class, Provided.class, Provides.class, Metadata.KNOWN_EXTENSIONS, Metadata.EXTENSION_EXTRACTOR); } catch (IllegalAccessException | ClassNotFoundException | NoSuchMethodException | NoSuchMethodError ignore) { // Old version of bootstrap method Class<?> bootstrap = Class.forName("com.jetbrains.bootstrap.JBRApiBootstrap"); a = (ServiceApi) (Object) lookup .findStatic(bootstrap, "bootstrap", MethodType.methodType(Object.class, MethodHandles.Lookup.class)) .invokeExact(lookup); } } catch (IllegalAccessException | ClassNotFoundException | NoSuchMethodException | NoSuchMethodError e) { exception = e; } catch (Throwable e) { Throwable t = e.getCause(); if (t instanceof Error) throw (Error) t; else throw new Error(t); } api = a; bootstrapException = exception; IMPL_VERSION = api == null ? "UNKNOWN" : api.getImplVersion(); } private static final String IMPL_VERSION, API_VERSION = getApiVersionFromModule(); private static String getApiVersionFromModule() { java.lang.module.ModuleDescriptor descriptor = JBR.class.getModule().getDescriptor(); if (descriptor != null && descriptor.version().isPresent()) { return descriptor.version().get().toString(); } else { return "SNAPSHOT"; } } private JBR() {} private static <T> T getServiceWithFallback(Class<T> interFace, FallbackSupplier<T> fallback, Extensions... extensions) { T service = getService(interFace, extensions); try { return service != null ? service : fallback != null ? fallback.get() : null; } catch (Throwable ignore) { return null; } } static <T> T getService(Class<T> interFace, Extensions... extensions) { return api == null ? null : api.getService(interFace, extensions); } /** * Checks whether JBR API is available at runtime. * @return true when running on JBR which implements JBR API */ public static boolean isAvailable() { return api != null; } /** * Returns JBR API version. * Development versions of JBR API return "SNAPSHOT". * When running on Java 8, returns "UNKNOWN". * <h4>Note:</h4> * This is an API version, which comes with client application, it is *almost* * a compile-time constant and has nothing to do with JRE it runs on. * @return JBR API version in form {@code MAJOR.MINOR.PATCH}, or "SNAPSHOT" / "UNKNOWN". */ public static String getApiVersion() { return API_VERSION; } /** * Returns JBR API version supported by current runtime or "UNKNOWN". * <h4>Note:</h4> * This method can return "UNKNOWN" even when JBR API {@link #isAvailable()}. * @return JBR API version supported by current implementation or "UNKNOWN". */ public static String getImplVersion() { return IMPL_VERSION; } /** * Checks whether given {@linkplain com.jetbrains.Extensions extension} is supported. * @param extension extension to check * @return true if extension is supported */ public static boolean isExtensionSupported(Extensions extension) { return api != null && api.isExtensionSupported(extension); } /** * Internal API interface, contains most basic methods for communication between client and JBR. */ @Service @Provided private interface ServiceApi { <T> T getService(Class<T> interFace); default <T> T getService(Class<T> interFace, Enum<?>... extensions) { return extensions.length == 0 ? getService(interFace) : null; } default String getImplVersion() { return "UNKNOWN"; } default boolean isExtensionSupported(Enum<?> extension) { return false; } } @FunctionalInterface private interface FallbackSupplier<T> { T get() throws Throwable; } // ========================== Generated metadata ========================== /** * Generated client-side metadata, needed by JBR when linking the implementation. */ @SuppressWarnings({"rawtypes", "deprecation"}) private static final class Metadata { // Needed only for compatibility. private static final String[] KNOWN_SERVICES = {"com.jetbrains.JBR$ServiceApi", /*KNOWN_SERVICES*/}; private static final String[] KNOWN_PROXIES = {/*KNOWN_PROXIES*/}; private static final Function<java.lang.reflect.Method, Extensions> EXTENSION_EXTRACTOR = m -> { Extension e = m.getAnnotation(Extension.class); return e == null ? null : e.value(); }; private static final Map<Extensions, Class[]> KNOWN_EXTENSIONS = new EnumMap<>(Extensions.class); static { /*KNOWN_EXTENSIONS*/ for (Extensions e : Extensions.values()) KNOWN_EXTENSIONS.putIfAbsent(e, new Class[0]); } } // ======================= Generated static methods ======================= /*GENERATED_METHODS*/ }