| /******************************************************************************* |
| * Copyright (c) 2018 The Eclipse Foundation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * The Eclipse Foundation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.epp.mpc.rest.client.internal.resteasy; |
| |
| import static java.util.Objects.requireNonNull; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.net.URI; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.util.function.Function; |
| |
| import javax.ws.rs.client.WebTarget; |
| |
| import org.apache.http.protocol.BasicHttpContext; |
| import org.apache.http.protocol.HttpContext; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.epp.mpc.rest.client.IRestClient; |
| import org.eclipse.epp.mpc.rest.client.internal.util.RuntimeCoreException; |
| import org.jboss.resteasy.client.jaxrs.ProxyBuilder; |
| |
| public class ApacheHttpClientRestClientImpl<E> implements IRestClient<E> { |
| |
| public static final String CONTEXT_PROVIDER_KEY = ApacheHttpClientRestClientImpl.class + ".httpContextProvider"; |
| |
| public static final String CONTEXT_MONITOR_KEY = ApacheHttpClientRestClientImpl.class |
| + ".httpContext.progressMonitor"; |
| |
| private final HttpContext defaultContext; |
| |
| private final WebTarget webTarget; |
| |
| private final Function<IProgressMonitor, E> monitoredEndpointSupplier; |
| |
| private final E endpoint; |
| |
| public ApacheHttpClientRestClientImpl(Class<E> endpointClass, WebTarget target, HttpContext defaultContext) { |
| this.defaultContext = defaultContext == null ? new BasicHttpContext() : defaultContext; |
| this.endpoint = ProxyBuilder.builder(endpointClass, target).build(); |
| this.webTarget = target; |
| this.monitoredEndpointSupplier = createMonitoredEndpointSupplier(endpointClass, target, this.defaultContext, |
| this.endpoint); |
| } |
| |
| private static <E> Function<IProgressMonitor, E> createMonitoredEndpointSupplier(Class<E> endpointClass, |
| WebTarget target, HttpContext defaultContext, E endpoint) { |
| HttpContextInjector contextProvider = (HttpContextInjector) target.getConfiguration() |
| .getProperty(CONTEXT_PROVIDER_KEY); |
| |
| if (contextProvider == null) { |
| return null; |
| } |
| @SuppressWarnings("unchecked") |
| Class<? extends E> proxyClass = (Class<? extends E>) Proxy.getProxyClass(endpointClass.getClassLoader(), |
| endpointClass); |
| Constructor<? extends E> constructor; |
| try { |
| constructor = proxyClass.getConstructor(InvocationHandler.class); |
| } catch (NoSuchMethodException e) { |
| throw new InternalError(e.toString(), e); |
| } |
| InvocationHandler endpointInvocationHandler = Proxy.getInvocationHandler(endpoint); |
| |
| return monitor -> { |
| InvocationHandler monitoredInvocationHandler = new MonitoredInvocationHandler(endpointInvocationHandler, |
| contextProvider, monitor, defaultContext); |
| try { |
| return constructor.newInstance(monitoredInvocationHandler); |
| } catch (IllegalAccessException | InstantiationException e) { |
| throw new InternalError(e.toString(), e); |
| } catch (InvocationTargetException e) { |
| Throwable t = e.getCause(); |
| if (t instanceof RuntimeException) { |
| throw (RuntimeException) t; |
| } else { |
| throw new InternalError(t.toString(), t); |
| } |
| } |
| }; |
| } |
| |
| @Override |
| public URI getBaseUri() { |
| return webTarget.getUri(); |
| } |
| |
| @Override |
| public E call() { |
| return endpoint; |
| } |
| |
| @Override |
| public E call(IProgressMonitor monitor) throws UnsupportedOperationException, RuntimeCoreException { |
| if (monitoredEndpointSupplier == null) { |
| throw new UnsupportedOperationException(); |
| } |
| return monitoredEndpointSupplier.apply(monitor); |
| } |
| |
| private static final class MonitoredInvocationHandler implements InvocationHandler { |
| |
| private final InvocationHandler delegate; |
| |
| private final AtomicReference<IProgressMonitor> monitorReference; |
| |
| private final HttpContextInjector contextProvider; |
| |
| private final HttpContext parentContext; |
| |
| public MonitoredInvocationHandler(InvocationHandler delegate, HttpContextInjector contextProvider, |
| IProgressMonitor monitor, HttpContext parentContext) { |
| requireNonNull(delegate, "delegate"); |
| requireNonNull(delegate, "contextProvider"); |
| requireNonNull(monitor, "monitor"); |
| requireNonNull(monitor, "parentContext"); |
| this.delegate = delegate; |
| this.contextProvider = contextProvider; |
| this.monitorReference = new AtomicReference<>(monitor); |
| this.parentContext = parentContext; |
| } |
| |
| @Override |
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
| IProgressMonitor monitor = monitorReference.getAndSet(null); |
| if (monitor == null) { |
| throw new IllegalStateException("monitor not set or already consumed"); //$NON-NLS-1$ |
| } |
| |
| HttpContext monitoredContext = new BasicHttpContext(parentContext); |
| monitoredContext.setAttribute(CONTEXT_MONITOR_KEY, monitor); |
| |
| Callable<?> delegateInvoke = () -> { |
| try { |
| return delegate.invoke(proxy, method, args); |
| } catch (Exception ex) { |
| throw ex; |
| } catch (Throwable t) { |
| throw (Error) t; |
| } |
| }; |
| return contextProvider.withContext(monitoredContext, delegateInvoke); |
| } |
| } |
| } |