io.scalecube.services.ServiceCall Maven / Gradle / Ivy
package io.scalecube.services;
import io.scalecube.services.api.ErrorData;
import io.scalecube.services.api.ServiceMessage;
import io.scalecube.services.exceptions.DefaultErrorMapper;
import io.scalecube.services.exceptions.ServiceClientErrorMapper;
import io.scalecube.services.exceptions.ServiceUnavailableException;
import io.scalecube.services.methods.MethodInfo;
import io.scalecube.services.methods.ServiceMethodInvoker;
import io.scalecube.services.methods.ServiceMethodRegistry;
import io.scalecube.services.registry.api.ServiceRegistry;
import io.scalecube.services.routing.Router;
import io.scalecube.services.routing.Routers;
import io.scalecube.services.transport.api.ClientTransport;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class ServiceCall {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCall.class);
private ClientTransport transport;
private ServiceMethodRegistry methodRegistry;
private ServiceRegistry serviceRegistry;
private Router router;
private ServiceClientErrorMapper errorMapper = DefaultErrorMapper.INSTANCE;
private Map credentials = Collections.emptyMap();
private String contentType = ServiceMessage.DEFAULT_DATA_FORMAT;
public ServiceCall() {}
private ServiceCall(ServiceCall other) {
this.transport = other.transport;
this.methodRegistry = other.methodRegistry;
this.serviceRegistry = other.serviceRegistry;
this.router = other.router;
this.errorMapper = other.errorMapper;
this.contentType = other.contentType;
this.credentials = Collections.unmodifiableMap(new HashMap<>(other.credentials));
}
/**
* Setter for {@code clientTransport}.
*
* @param clientTransport client transport.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall transport(ClientTransport clientTransport) {
ServiceCall target = new ServiceCall(this);
target.transport = clientTransport;
return target;
}
/**
* Setter for {@code serviceRegistry}.
*
* @param serviceRegistry service registry.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall serviceRegistry(ServiceRegistry serviceRegistry) {
ServiceCall target = new ServiceCall(this);
target.serviceRegistry = serviceRegistry;
return target;
}
/**
* Setter for {@code methodRegistry}.
*
* @param methodRegistry method registry.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall methodRegistry(ServiceMethodRegistry methodRegistry) {
ServiceCall target = new ServiceCall(this);
target.methodRegistry = methodRegistry;
return target;
}
/**
* Setter for {@code routerType}.
*
* @param routerType method registry.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall router(Class extends Router> routerType) {
ServiceCall target = new ServiceCall(this);
target.router = Routers.getRouter(routerType);
return target;
}
/**
* Setter for {@code router}.
*
* @param router router.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall router(Router router) {
ServiceCall target = new ServiceCall(this);
target.router = router;
return target;
}
/**
* Setter for {@code errorMapper}.
*
* @param errorMapper error mapper.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall errorMapper(ServiceClientErrorMapper errorMapper) {
ServiceCall target = new ServiceCall(this);
target.errorMapper = errorMapper;
return target;
}
/**
* Setter for {@code credentials}.
*
* @param credentials credentials.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall credentials(Map credentials) {
ServiceCall target = new ServiceCall(this);
target.credentials = Collections.unmodifiableMap(new HashMap<>(credentials));
return target;
}
/**
* Setter for {@code contentType}.
*
* @param contentType content type.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall contentType(String contentType) {
ServiceCall target = new ServiceCall(this);
target.contentType = contentType;
return target;
}
/**
* Issues fire-and-forget request.
*
* @param request request message to send.
* @return mono publisher completing normally or with error.
*/
public Mono oneWay(ServiceMessage request) {
return Mono.defer(() -> requestOne(request, Void.class).then());
}
/**
* Issues request-and-reply request.
*
* @param request request message to send.
* @return mono publisher completing with single response message or with error.
*/
public Mono requestOne(ServiceMessage request) {
return requestOne(request, null);
}
/**
* Issues request-and-reply request.
*
* @param request request message to send.
* @param responseType type of response (optional).
* @return mono publisher completing with single response message or with error.
*/
public Mono requestOne(ServiceMessage request, Type responseType) {
return Mono.defer(
() -> {
ServiceMethodInvoker methodInvoker;
if (methodRegistry != null
&& (methodInvoker = methodRegistry.getInvoker(request.qualifier())) != null) {
// local service
return methodInvoker.invokeOne(request).map(this::throwIfError);
} else {
// remote service
Objects.requireNonNull(transport, "[requestOne] transport");
return Mono.fromCallable(() -> serviceLookup(request))
.flatMap(
serviceReference ->
transport
.create(serviceReference)
.requestResponse(request, responseType)
.map(this::throwIfError));
}
});
}
/**
* Issues request to service which returns stream of service messages back.
*
* @param request request message to send.
* @return flux publisher of service responses.
*/
public Flux requestMany(ServiceMessage request) {
return requestMany(request, null);
}
/**
* Issues request to service which returns stream of service messages back.
*
* @param request request with given headers.
* @param responseType type of responses (optional).
* @return flux publisher of service responses.
*/
public Flux requestMany(ServiceMessage request, Type responseType) {
return Flux.defer(
() -> {
ServiceMethodInvoker methodInvoker;
if (methodRegistry != null
&& (methodInvoker = methodRegistry.getInvoker(request.qualifier())) != null) {
// local service
return methodInvoker.invokeMany(request).map(this::throwIfError);
} else {
// remote service
Objects.requireNonNull(transport, "[requestMany] transport");
return Mono.fromCallable(() -> serviceLookup(request))
.flatMapMany(
serviceReference ->
transport
.create(serviceReference)
.requestStream(request, responseType)
.map(this::throwIfError));
}
});
}
/**
* Issues stream of service requests to service which returns stream of service messages back.
*
* @param publisher of service requests.
* @return flux publisher of service responses.
*/
public Flux requestBidirectional(Publisher publisher) {
return requestBidirectional(publisher, null);
}
/**
* Issues stream of service requests to service which returns stream of service messages back.
*
* @param publisher of service requests.
* @param responseType type of responses (optional).
* @return flux publisher of service responses.
*/
public Flux requestBidirectional(
Publisher publisher, Type responseType) {
return Flux.from(publisher)
.switchOnFirst(
(first, messages) -> {
if (first.hasValue()) {
ServiceMessage request = first.get();
ServiceMethodInvoker methodInvoker;
if (methodRegistry != null
&& (methodInvoker = methodRegistry.getInvoker(request.qualifier())) != null) {
// local service
return methodInvoker.invokeBidirectional(messages).map(this::throwIfError);
} else {
// remote service
Objects.requireNonNull(transport, "[requestBidirectional] transport");
return Mono.fromCallable(() -> serviceLookup(request))
.flatMapMany(
serviceReference ->
transport
.create(serviceReference)
.requestChannel(messages, responseType)
.map(this::throwIfError));
}
}
return messages;
});
}
/**
* Create proxy creates a java generic proxy instance by a given service interface.
*
* @param serviceInterface Service Interface type.
* @return newly created service proxy object.
*/
@SuppressWarnings("unchecked")
public T api(Class serviceInterface) {
final ServiceCall serviceCall = this;
final Map genericReturnTypes = Reflect.methodsInfo(serviceInterface);
// noinspection unchecked,Convert2Lambda
return (T)
Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[] {serviceInterface},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] params) {
Optional © 2015 - 2025 Weber Informatics LLC | Privacy Policy