blob: bb1609c78e97dc9fb3c15d8dc70a9d57e23f0ed8 [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.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import org.eclipse.lsp4j.jsonrpc.Endpoint;
import org.eclipse.lsp4j.jsonrpc.services.AnnotationUtil.DelegateInfo;
import org.eclipse.lsp4j.jsonrpc.services.AnnotationUtil.MethodInfo;
/**
* A Proxy that wraps an {@link Endpoint} in one or more service interfaces, i.e. interfaces
* containing {@link JsonNotification} and {@link JsonRequest} methods.
*/
public class EndpointProxy implements InvocationHandler {
private final Method object_equals;
private final Method object_hashCode;
private final Method object_toString;
private final Endpoint delegate;
private final LinkedHashMap<String, MethodInfo> methodInfos;
private final LinkedHashMap<String, DelegateInfo> delegatedSegments;
public EndpointProxy(Endpoint delegate, Class<?> interface_) {
this(delegate, Collections.singletonList(interface_));
}
public EndpointProxy(Endpoint delegate, Collection<Class<?>> interfaces) {
if (delegate == null)
throw new NullPointerException("delegate");
if (interfaces == null)
throw new NullPointerException("interfaces");
if (interfaces.isEmpty())
throw new IllegalArgumentException("interfaces must not be empty.");
this.delegate = delegate;
try {
object_equals = Object.class.getDeclaredMethod("equals", Object.class);
object_hashCode = Object.class.getDeclaredMethod("hashCode");
object_toString = Object.class.getDeclaredMethod("toString");
} catch (NoSuchMethodException | SecurityException exception) {
throw new RuntimeException(exception);
}
methodInfos = new LinkedHashMap<>();
delegatedSegments = new LinkedHashMap<>();
for (Class<?> interf : interfaces) {
AnnotationUtil.findRpcMethods(interf, new HashSet<Class<?>>(), (methodInfo) -> {
if (methodInfos.put(methodInfo.method.getName(), methodInfo) != null) {
throw new IllegalStateException("Duplicate RPC method " + methodInfo.method);
}
});
AnnotationUtil.findDelegateSegments(interf, new HashSet<Class<?>>(), (method) -> {
Object delegateProxy = ServiceEndpoints.toServiceObject(delegate, method.getReturnType());
DelegateInfo info = new DelegateInfo();
info.delegate = delegateProxy;
info.method = method;
if (delegatedSegments.put(method.getName(), info) != null) {
throw new IllegalStateException("Duplicate RPC method " + method);
}
});
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
args = args == null ? new Object[0] : args;
MethodInfo methodInfo = this.methodInfos.get(method.getName());
if (methodInfo != null) {
Object params = getParams(args, methodInfo);
if (methodInfo.isNotification) {
delegate.notify(methodInfo.name, params);
return null;
}
return delegate.request(methodInfo.name, params);
}
DelegateInfo delegateInfo = this.delegatedSegments.get(method.getName());
if (delegateInfo != null) {
return delegateInfo.delegate;
}
if (object_equals.equals(method) && args.length == 1) {
if(args[0] != null ) {
try {
return this.equals(Proxy.getInvocationHandler(args[0]));
} catch (IllegalArgumentException exception) {
}
}
return this.equals(args[0]);
}
if (object_hashCode.equals(method)) {
return this.hashCode();
}
if (object_toString.equals(method)) {
return this.toString();
}
return method.invoke(delegate, args);
}
protected Object getParams(Object[] args, MethodInfo methodInfo) {
if (args.length == 0) {
return null;
}
if (args.length == 1) {
return args[0];
}
return Arrays.asList(args);
}
@Override
public String toString() {
return getClass().getSimpleName() + " for " + delegate.toString();
}
}