package org.jboss.resteasy.client.jaxrs.internal.proxy;

import org.jboss.resteasy.client.jaxrs.ProxyConfig;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.jboss.resteasy.client.jaxrs.internal.ClientConfiguration;
import org.jboss.resteasy.client.jaxrs.internal.ClientInvocation;
import org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder;
import org.jboss.resteasy.client.jaxrs.internal.ClientResponse;
import org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.ClientContext;
import org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.DefaultEntityExtractorFactory;
import org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.EntityExtractor;
import org.jboss.resteasy.client.jaxrs.internal.proxy.processors.InvocationProcessor;
import org.jboss.resteasy.client.jaxrs.internal.proxy.processors.ProcessorFactory;
import org.jboss.resteasy.client.jaxrs.internal.proxy.processors.WebTargetProcessor;
import org.jboss.resteasy.util.FeatureContextDelegate;
import org.jboss.resteasy.util.MediaTypeHelper;

import javax.ws.rs.Path;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.RxInvoker;
import javax.ws.rs.client.RxInvokerProvider;
import javax.ws.rs.client.SyncInvoker;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.concurrent.ExecutorService;
import java.util.Set;

Author:Bill Burke
Version:$Revision: 1 $
/** * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */
public class ClientInvoker implements MethodInvoker { protected String httpMethod; protected Method method; protected Class<?> declaring; protected MediaType[] accepts; protected Object[] processors; protected ResteasyWebTarget webTarget; protected boolean followRedirects; @SuppressWarnings("rawtypes") protected EntityExtractor extractor; protected DefaultEntityExtractorFactory entityExtractorFactory; protected ClientConfiguration invokerConfig; protected RxInvokerProvider<?> rxInvokerProvider; protected SyncInvoker syncInvoker; public ClientInvoker(final ResteasyWebTarget parent, final Class<?> declaring, final Method method, final ProxyConfig config) { // webTarget must be a clone so that it has a cloned ClientConfiguration so we can apply DynamicFeature if (method.isAnnotationPresent(Path.class)) { this.webTarget = parent.path(method); } else { this.webTarget = parent.clone(); } this.declaring = declaring; this.method = method; invokerConfig = (ClientConfiguration) this.webTarget.getConfiguration(); ResourceInfo info = new ResourceInfo() { @Override public Method getResourceMethod() { return ClientInvoker.this.method; } @Override public Class<?> getResourceClass() { return ClientInvoker.this.declaring; } }; Set<DynamicFeature> dynamicFeatures = invokerConfig.getDynamicFeatures(); if (null != dynamicFeatures) { for (DynamicFeature feature : dynamicFeatures) { feature.configure(info, new FeatureContextDelegate(invokerConfig)); } } this.processors = ProcessorFactory.createProcessors(declaring, method, invokerConfig, config.getDefaultConsumes()); accepts = MediaTypeHelper.getProduces(declaring, method, config.getDefaultProduces()); entityExtractorFactory = new DefaultEntityExtractorFactory(); this.extractor = entityExtractorFactory.createExtractor(method); rxInvokerProvider = invokerConfig.getRxInvokerProviderFromReactiveClass(method.getReturnType()); } public MediaType[] getAccepts() { return accepts; } public Method getMethod() { return method; } public Class<?> getDeclaring() { return declaring; } public Object invoke(Object[] args) { return rxInvokerProvider != null ? invokeAsync(args) : invokeSync(args); } protected Object invokeAsync(final Object[] args) { ClientInvocation request = createRequest(args); WebTarget t = request.getActualTarget(); ClientInvocationBuilder builder = (ClientInvocationBuilder) (t != null ? t : webTarget).request(); builder.setClientInvocation(request); ExecutorService executor = webTarget.getResteasyClient().getScheduledExecutor(); if (executor == null) { executor = request.asyncInvocationExecutor(); } RxInvoker<?> rxInvoker = (RxInvoker<?>) rxInvokerProvider.getRxInvoker(builder, executor); Type type = method.getGenericReturnType(); if (type instanceof ParameterizedType) { type = ((ParameterizedType) type).getActualTypeArguments()[0]; } GenericType<?> gt = new GenericType(type); Object e = request.getEntity(); Object o = null; if (e != null) { o = rxInvoker.method(getHttpMethod(), Entity.entity(e, request.getHeaders().getMediaType()), gt); } else { o = rxInvoker.method(getHttpMethod(), gt); } return o; } protected Object invokeSync(Object[] args) { ClientInvocation request = createRequest(args); ClientResponse response = (ClientResponse)request.invoke(); ClientContext context = new ClientContext(request, response, entityExtractorFactory); return extractor.extractEntity(context); } protected ClientInvocation createRequest(Object[] args) { WebTarget target = this.webTarget; for (int i = 0; i < processors.length; i++) { if (processors != null && processors[i] instanceof WebTargetProcessor) { WebTargetProcessor processor = (WebTargetProcessor)processors[i]; target = processor.build(target, args[i]); } } ClientInvocationBuilder builder = (ClientInvocationBuilder) target.request(); ClientInvocation clientInvocation = (ClientInvocation) builder.build(httpMethod); clientInvocation.setClientInvoker(this); if (target != this.webTarget) { clientInvocation.setActualTarget(target); } if (accepts != null) { clientInvocation.getHeaders().accept(accepts); } for (int i = 0; i < processors.length; i++) { if (processors != null && processors[i] instanceof InvocationProcessor) { InvocationProcessor processor = (InvocationProcessor)processors[i]; processor.process(clientInvocation, args[i]); } } return clientInvocation; } public String getHttpMethod() { return httpMethod; } public void setHttpMethod(String httpMethod) { this.httpMethod = httpMethod; } public boolean isFollowRedirects() { return followRedirects; } public void setFollowRedirects(boolean followRedirects) { this.followRedirects = followRedirects; } public void followRedirects() { setFollowRedirects(true); } public SyncInvoker getSyncInvoker() { return syncInvoker; } public void setSyncInvoker(SyncInvoker syncInvoker) { this.syncInvoker = syncInvoker; } }