diff --git a/examples/strict/pom.xml b/examples/strict/pom.xml index 17db2fdf66d8f..0d16d5495f6e1 100644 --- a/examples/strict/pom.xml +++ b/examples/strict/pom.xml @@ -86,6 +86,10 @@ io.reactivex.rxjava2 rxjava + + org.jboss.shamrock + shamrock-rest-client-deployment + diff --git a/examples/strict/src/main/java/org/jboss/shamrock/example/rest/RestInterface.java b/examples/strict/src/main/java/org/jboss/shamrock/example/rest/RestInterface.java new file mode 100644 index 0000000000000..6b598105019c8 --- /dev/null +++ b/examples/strict/src/main/java/org/jboss/shamrock/example/rest/RestInterface.java @@ -0,0 +1,12 @@ +package org.jboss.shamrock.example.rest; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +@Path("/foo") +public interface RestInterface { + + @GET + void get(); + +} diff --git a/examples/strict/src/main/java/org/jboss/shamrock/example/rest/TestResource.java b/examples/strict/src/main/java/org/jboss/shamrock/example/rest/TestResource.java index c81a17c737549..48bcc2be07497 100644 --- a/examples/strict/src/main/java/org/jboss/shamrock/example/rest/TestResource.java +++ b/examples/strict/src/main/java/org/jboss/shamrock/example/rest/TestResource.java @@ -9,12 +9,14 @@ import javax.xml.bind.annotation.XmlRootElement; import io.reactivex.Single; +import org.eclipse.microprofile.rest.client.RestClientBuilder; @Path("/test") public class TestResource { @GET public String getTest() { + RestInterface iface = RestClientBuilder.newBuilder().build(RestInterface.class); return "TEST"; } diff --git a/rest-client/runtime/src/main/java/org/jboss/shamrock/restclient/runtime/ProxyInvocationHandler.java b/rest-client/runtime/src/main/java/org/jboss/shamrock/restclient/runtime/ProxyInvocationHandler.java deleted file mode 100644 index 800e1749fc6ff..0000000000000 --- a/rest-client/runtime/src/main/java/org/jboss/shamrock/restclient/runtime/ProxyInvocationHandler.java +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Copyright 2015-2017 Red Hat, Inc, and individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.shamrock.restclient.runtime; - -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -import javax.enterprise.context.spi.CreationalContext; -import javax.enterprise.inject.spi.BeanManager; -import javax.enterprise.inject.spi.CDI; -import javax.enterprise.inject.spi.InterceptionType; -import javax.enterprise.inject.spi.Interceptor; -import javax.ws.rs.client.ResponseProcessingException; -import javax.ws.rs.ext.ParamConverter; -import javax.ws.rs.ext.ParamConverterProvider; - -import org.jboss.logging.Logger; -import org.jboss.resteasy.client.jaxrs.ResteasyClient; - -import RestClientProxy; - -/** - * Created by hbraun on 22.01.18. - */ -class ProxyInvocationHandler implements InvocationHandler { - - private static final Logger LOGGER = Logger.getLogger(ProxyInvocationHandler.class); - - private final Object target; - - private final Set providerInstances; - - private final Map> interceptorChains; - - private final ResteasyClient client; - - private final CreationalContext creationalContext; - - private final AtomicBoolean closed; - - public ProxyInvocationHandler(Class restClientInterface, Object target, Set providerInstances, ResteasyClient client) { - this.target = target; - this.providerInstances = providerInstances; - this.client = client; - this.closed = new AtomicBoolean(); - BeanManager beanManager = getBeanManager(restClientInterface); - if (beanManager != null) { - this.creationalContext = beanManager.createCreationalContext(null); - this.interceptorChains = initInterceptorChains(beanManager, creationalContext, restClientInterface); - } else { - this.creationalContext = null; - this.interceptorChains = Collections.emptyMap(); - } - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (RestClientProxy.class.equals(method.getDeclaringClass())) { - return invokeRestClientProxyMethod(proxy, method, args); - } - if (closed.get()) { - throw new IllegalStateException("RestClientProxy is closed"); - } - - boolean replacementNeeded = false; - Object[] argsReplacement = args != null ? new Object[args.length] : null; - Annotation[][] parameterAnnotations = method.getParameterAnnotations(); - - for (Object p : providerInstances) { - if (p instanceof ParamConverterProvider) { - - int index = 0; - for (Object arg : args) { - - if (parameterAnnotations[index].length > 0) { // does a parameter converter apply? - - ParamConverter converter = ((ParamConverterProvider) p).getConverter(arg.getClass(), null, parameterAnnotations[index]); - if (converter != null) { - Type[] genericTypes = getGenericTypes(converter.getClass()); - if (genericTypes.length == 1) { - - // minimum supported types - switch (genericTypes[0].getTypeName()) { - case "java.lang.String": - ParamConverter stringConverter = (ParamConverter) converter; - argsReplacement[index] = stringConverter.toString((String) arg); - replacementNeeded = true; - break; - case "java.lang.Integer": - ParamConverter intConverter = (ParamConverter) converter; - argsReplacement[index] = intConverter.toString((Integer) arg); - replacementNeeded = true; - break; - case "java.lang.Boolean": - ParamConverter boolConverter = (ParamConverter) converter; - argsReplacement[index] = boolConverter.toString((Boolean) arg); - replacementNeeded = true; - break; - default: - continue; - } - } - } - } else { - argsReplacement[index] = arg; - } - index++; - } - } - } - - if (replacementNeeded) { - args = argsReplacement; - } - - List chain = interceptorChains.get(method); - if (chain != null) { - // Invoke business method interceptors - return new InvocationContextImpl(target, method, args, chain).proceed(); - } else { - try { - return method.invoke(target, args); - } catch (InvocationTargetException e) { - if (e.getCause() instanceof ResponseProcessingException) { - ResponseProcessingException rpe = (ResponseProcessingException) e.getCause(); - Throwable cause = rpe.getCause(); - if (cause instanceof RuntimeException) { - throw cause; - } - } - throw e; - } - } - } - - private Object invokeRestClientProxyMethod(Object proxy, Method method, Object[] args) throws Throwable { - switch (method.getName()) { - case "getClient": - return client; - case "close": - close(); - return null; - default: - throw new IllegalStateException("Unsupported RestClientProxy method: " + method); - } - } - - private void close() { - if (closed.compareAndSet(false, true)) { - if (creationalContext != null) { - creationalContext.release(); - } - client.close(); - } - } - - private Type[] getGenericTypes(Class aClass) { - Type[] genericInterfaces = aClass.getGenericInterfaces(); - Type[] genericTypes = new Type[] {}; - for (Type genericInterface : genericInterfaces) { - if (genericInterface instanceof ParameterizedType) { - genericTypes = ((ParameterizedType) genericInterface).getActualTypeArguments(); - } - } - return genericTypes; - } - - private static List getBindings(Annotation[] annotations, BeanManager beanManager) { - if (annotations.length == 0) { - return Collections.emptyList(); - } - List bindings = new ArrayList<>(); - for (Annotation annotation : annotations) { - if (beanManager.isInterceptorBinding(annotation.annotationType())) { - bindings.add(annotation); - } - } - return bindings; - } - - private static BeanManager getBeanManager(Class restClientInterface) { - try { - return CDI.current().getBeanManager(); - } catch (IllegalStateException e) { - LOGGER.warnf("CDI container is not available - interceptor bindings declared on %s will be ignored", restClientInterface.getSimpleName()); - return null; - } - } - - private static Map> initInterceptorChains(BeanManager beanManager, CreationalContext creationalContext, Class restClientInterface) { - - Map> chains = new HashMap<>(); - // Interceptor as a key in a map is not entirely correct (custom interceptors) but should work in most cases - Map, Object> interceptorInstances = new HashMap<>(); - - List classLevelBindings = getBindings(restClientInterface.getAnnotations(), beanManager); - - for (Method method : restClientInterface.getMethods()) { - if (method.isDefault() || Modifier.isStatic(method.getModifiers())) { - continue; - } - List methodLevelBindings = getBindings(method.getAnnotations(), beanManager); - - if (!classLevelBindings.isEmpty() || !methodLevelBindings.isEmpty()) { - - Annotation[] interceptorBindings = merge(methodLevelBindings, classLevelBindings); - - List> interceptors = beanManager.resolveInterceptors(InterceptionType.AROUND_INVOKE, interceptorBindings); - if (!interceptors.isEmpty()) { - List chain = new ArrayList<>(); - for (Interceptor interceptor : interceptors) { - chain.add(new InvocationContextImpl.InterceptorInvocation(interceptor, - interceptorInstances.computeIfAbsent(interceptor, i -> beanManager.getReference(i, i.getBeanClass(), creationalContext)))); - } - chains.put(method, chain); - } - } - } - return chains.isEmpty() ? Collections.emptyMap() : chains; - } - - private static Annotation[] merge(List methodLevelBindings, List classLevelBindings) { - Set> types = methodLevelBindings.stream().map(a -> a.annotationType()).collect(Collectors.toSet()); - List merged = new ArrayList<>(methodLevelBindings); - for (Annotation annotation : classLevelBindings) { - if (!types.contains(annotation.annotationType())) { - merged.add(annotation); - } - } - return merged.toArray(new Annotation[] {}); - } - -} diff --git a/rest-client/runtime/src/main/java/org/jboss/shamrock/restclient/runtime/RestClientBuilderImpl.java b/rest-client/runtime/src/main/java/org/jboss/shamrock/restclient/runtime/RestClientBuilderImpl.java index 708ad2893ce4f..4d6f4f685843c 100644 --- a/rest-client/runtime/src/main/java/org/jboss/shamrock/restclient/runtime/RestClientBuilderImpl.java +++ b/rest-client/runtime/src/main/java/org/jboss/shamrock/restclient/runtime/RestClientBuilderImpl.java @@ -16,6 +16,7 @@ package org.jboss.shamrock.restclient.runtime; import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; @@ -140,7 +141,12 @@ public T build(Class aClass) throws IllegalStateException, RestClientDefi interfaces[0] = aClass; interfaces[1] = RestClientProxy.class; - return (T) Proxy.newProxyInstance(classLoader, interfaces, new ProxyInvocationHandler(aClass, actualClient, getLocalProviderInstances(), client)); + return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return method.invoke(actualClient, args); + } + }); } private boolean isMapperDisabled() {