blob: 81e5d87b4bbbca87fb093fdb44fe2b2272f4db1d [file] [log] [blame]
/*
* Copyright (c) 2019, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019 Payara Foundation and/or its affiliates. All rights reserved.
*
* 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.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.jersey.microprofile.restclient;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.client.WebTarget;
/**
* Model of the rest client interface.
*
* @author David Kral
* @author Patrik Dudits
*/
class RestClientModel {
private final InterfaceModel interfaceModel;
private final Map<Method, MethodModel> methodModels;
/**
* Creates new instance of the {@link RestClientModel} base on interface class.
*
* @param context RestClient data context
* @return new instance
*/
static RestClientModel from(RestClientContext context) {
InterfaceModel interfaceModel = InterfaceModel.from(context);
return new Builder()
.interfaceModel(interfaceModel)
.methodModels(parseMethodModels(interfaceModel))
.build();
}
private RestClientModel(Builder builder) {
this.interfaceModel = builder.classModel;
this.methodModels = builder.methodModels;
}
/**
* Invokes desired rest client method.
*
* @param baseWebTarget path to endpoint
* @param method desired method
* @param args actual method parameters
* @return method return value
*/
<T> Object invokeMethod(WebTarget baseWebTarget, Method method, Object[] args) {
WebTarget classLevelTarget = baseWebTarget.path(interfaceModel.getPath());
MethodModel methodModel = methodModels.get(method);
if (methodModel != null) {
return new InterceptorInvocationContext(classLevelTarget, methodModel, method, args).proceed();
}
try {
if (method.isDefault()) {
T instance = (T) ReflectionUtil.createProxyInstance(interfaceModel.getRestClientClass());
return method.invoke(instance, args);
} else {
throw new UnsupportedOperationException("This method is not supported!");
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Map<Method, MethodModel> parseMethodModels(InterfaceModel classModel) {
Map<Method, MethodModel> methodMap = new HashMap<>();
for (Method method : classModel.getRestClientClass().getMethods()) {
if (method.isDefault() || Modifier.isStatic(method.getModifiers())) {
continue;
}
//Skip method processing if method does not have HTTP annotation
//and is not sub resource (does not have Path annotation)
methodMap.put(method, MethodModel.from(classModel, method));
}
return methodMap;
}
private static class Builder {
private InterfaceModel classModel;
private Map<Method, MethodModel> methodModels;
private Builder() {
}
/**
* Rest client class converted to {@link InterfaceModel}
*
* @param classModel {@link InterfaceModel} instance
* @return Updated Builder instance
*/
Builder interfaceModel(InterfaceModel classModel) {
this.classModel = classModel;
return this;
}
/**
* Rest client class methods converted to {@link Map} of {@link MethodModel}
*
* @param methodModels Method models
* @return Updated Builder instance
*/
Builder methodModels(Map<Method, MethodModel> methodModels) {
this.methodModels = methodModels;
return this;
}
/**
* Creates new RestClientModel instance.
*
* @return new instance
*/
public RestClientModel build() {
return new RestClientModel(this);
}
}
@Override
public String toString() {
return interfaceModel.getRestClientClass().getName();
}
}