blob: c8ad3f0116820e3cffbc2e55510763fc2156ab3c [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2016 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
******************************************************************************/
package org.eclipse.lsp4j.jsonrpc.services;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.lsp4j.jsonrpc.Endpoint;
import org.eclipse.lsp4j.jsonrpc.json.JsonRpcMethod;
import org.eclipse.lsp4j.jsonrpc.json.ResponseJsonAdapter;
import com.google.gson.TypeAdapterFactory;
public final class ServiceEndpoints {
private ServiceEndpoints() {}
/**
* Wraps a given {@link Endpoint} in the given service interface.
*
* @return the wrapped service object
*/
@SuppressWarnings("unchecked")
public static <T> T toServiceObject(Endpoint endpoint, Class<T> interface_) {
Class<?>[] interfArray = new Class[]{interface_, Endpoint.class};
EndpointProxy invocationHandler = new EndpointProxy(endpoint, interface_);
return (T) Proxy.newProxyInstance(interface_.getClassLoader(), interfArray, invocationHandler);
}
/**
* Wraps a given {@link Endpoint} in the given service interfaces.
*
* @return the wrapped service object
*/
public static Object toServiceObject(Endpoint endpoint, Collection<Class<?>> interfaces, ClassLoader classLoader) {
Class<?>[] interfArray = new Class[interfaces.size() + 1];
interfaces.toArray(interfArray);
interfArray[interfArray.length - 1] = Endpoint.class;
EndpointProxy invocationHandler = new EndpointProxy(endpoint, interfaces);
return Proxy.newProxyInstance(classLoader, interfArray, invocationHandler);
}
/**
* Wraps a given object with service annotations behind an {@link Endpoint} interface.
*
* @return the wrapped service endpoint
*/
public static Endpoint toEndpoint(Object serviceObject) {
return new GenericEndpoint(serviceObject);
}
/**
* Wraps a collection of objects with service annotations behind an {@link Endpoint} interface.
*
* @return the wrapped service endpoint
*/
public static Endpoint toEndpoint(Collection<Object> serviceObjects) {
return new GenericEndpoint(serviceObjects);
}
/**
* Finds all Json RPC methods on a given class.
*
* @return the supported JsonRpcMethods
*/
public static Map<String, JsonRpcMethod> getSupportedMethods(Class<?> type) {
Set<Class<?>> visitedTypes = new HashSet<>();
return getSupportedMethods(type, visitedTypes);
}
/**
* Finds all Json RPC methods on a given type
*/
private static Map<String, JsonRpcMethod> getSupportedMethods(Class<?> type, Set<Class<?>> visitedTypes) {
Map<String, JsonRpcMethod> result = new LinkedHashMap<String, JsonRpcMethod>();
AnnotationUtil.findRpcMethods(type, visitedTypes, (methodInfo) -> {
JsonRpcMethod meth;
if (methodInfo.isNotification) {
meth = JsonRpcMethod.notification(methodInfo.name, methodInfo.parameterTypes);
} else {
Type genericReturnType = methodInfo.method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {
Type returnType = ((ParameterizedType) genericReturnType).getActualTypeArguments()[0];
TypeAdapterFactory responseTypeAdapter = null;
ResponseJsonAdapter responseTypeAdapterAnnotation = methodInfo.method.getAnnotation(ResponseJsonAdapter.class);
if (responseTypeAdapterAnnotation != null) {
try {
responseTypeAdapter = responseTypeAdapterAnnotation.value().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
meth = JsonRpcMethod.request(methodInfo.name, returnType, responseTypeAdapter, methodInfo.parameterTypes);
} else {
throw new IllegalStateException("Expecting return type of CompletableFuture but was : " + genericReturnType);
}
}
if (result.put(methodInfo.name, meth) != null) {
throw new IllegalStateException("Duplicate RPC method "+methodInfo.name+".");
};
});
AnnotationUtil.findDelegateSegments(type, new HashSet<>(), (method)-> {
Map<String, JsonRpcMethod> supportedDelegateMethods = getSupportedMethods(method.getReturnType(), visitedTypes);
for (JsonRpcMethod meth : supportedDelegateMethods.values()) {
if (result.put(meth.getMethodName(), meth) != null) {
throw new IllegalStateException("Duplicate RPC method "+meth.getMethodName()+".");
};
}
});
return result;
}
}