mirror of
https://github.com/jlengrand/quarkus.git
synced 2026-03-10 08:41:22 +00:00
Add support for microprofile rest client
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
package org.jboss.shamrock.arc.deployment;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -60,15 +62,18 @@ public class ArcAnnotationProcessor implements ResourceProcessor {
|
||||
// Index bean classes registered by shamrock
|
||||
Indexer indexer = new Indexer();
|
||||
Set<DotName> additionalIndex = new HashSet<>();
|
||||
for (Class<?> beanClass : beanDeployment.getAdditionalBeans()) {
|
||||
for (String beanClass : beanDeployment.getAdditionalBeans()) {
|
||||
indexBeanClass(beanClass, indexer, beanArchiveIndex.getIndex(), additionalIndex);
|
||||
}
|
||||
CompositeIndex index = CompositeIndex.create(indexer.complete(), beanArchiveIndex.getIndex());
|
||||
Set<String> frameworkPackages = additionalIndex.stream().map(dotName -> {
|
||||
String name = dotName.toString();
|
||||
return name.toString().substring(0, name.lastIndexOf("."));
|
||||
return name.substring(0, name.lastIndexOf("."));
|
||||
}).collect(Collectors.toSet());
|
||||
|
||||
for (Map.Entry<String, byte[]> beanClass : beanDeployment.getGeneratedBeans().entrySet()) {
|
||||
indexBeanClass(beanClass.getKey(), indexer, beanArchiveIndex.getIndex(), additionalIndex, beanClass.getValue());
|
||||
}
|
||||
CompositeIndex index = CompositeIndex.create(indexer.complete(), beanArchiveIndex.getIndex());
|
||||
Builder builder = BeanProcessor.builder();
|
||||
builder.setIndex(index);
|
||||
builder.setAdditionalBeanDefiningAnnotations(additionalBeanDefiningAnnotations);
|
||||
@@ -126,15 +131,15 @@ public class ArcAnnotationProcessor implements ResourceProcessor {
|
||||
return RuntimePriority.ARC_DEPLOYMENT;
|
||||
}
|
||||
|
||||
private void indexBeanClass(Class<?> beanClass, Indexer indexer, IndexView shamrockIndex, Set<DotName> additionalIndex) {
|
||||
DotName beanClassName = DotName.createSimple(beanClass.getName());
|
||||
private void indexBeanClass(String beanClass, Indexer indexer, IndexView shamrockIndex, Set<DotName> additionalIndex) {
|
||||
DotName beanClassName = DotName.createSimple(beanClass);
|
||||
if (additionalIndex.contains(beanClassName)) {
|
||||
return;
|
||||
}
|
||||
ClassInfo beanInfo = shamrockIndex.getClassByName(beanClassName);
|
||||
if (beanInfo == null) {
|
||||
System.out.println("Index bean class: " + beanClass);
|
||||
try (InputStream stream = ArcAnnotationProcessor.class.getClassLoader().getResourceAsStream(beanClass.getName().replace('.', '/') + ".class")) {
|
||||
try (InputStream stream = ArcAnnotationProcessor.class.getClassLoader().getResourceAsStream(beanClass.replace('.', '/') + ".class")) {
|
||||
beanInfo = indexer.index(stream);
|
||||
additionalIndex.add(beanInfo.name());
|
||||
} catch (IOException e) {
|
||||
@@ -158,4 +163,35 @@ public class ArcAnnotationProcessor implements ResourceProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private void indexBeanClass(String beanClass, Indexer indexer, IndexView shamrockIndex, Set<DotName> additionalIndex, byte[] beanData) {
|
||||
DotName beanClassName = DotName.createSimple(beanClass);
|
||||
if (additionalIndex.contains(beanClassName)) {
|
||||
return;
|
||||
}
|
||||
ClassInfo beanInfo = shamrockIndex.getClassByName(beanClassName);
|
||||
if (beanInfo == null) {
|
||||
System.out.println("Index bean class: " + beanClass);
|
||||
try (InputStream stream = new ByteArrayInputStream(beanData)) {
|
||||
beanInfo = indexer.index(stream);
|
||||
additionalIndex.add(beanInfo.name());
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to index: " + beanClass);
|
||||
}
|
||||
} else {
|
||||
// The class could be indexed by shamrock - we still need to distinguish framework classes
|
||||
additionalIndex.add(beanClassName);
|
||||
}
|
||||
for (DotName annotationName : beanInfo.annotations().keySet()) {
|
||||
if (!additionalIndex.contains(annotationName) && shamrockIndex.getClassByName(annotationName) == null) {
|
||||
try (InputStream annotationStream = ArcAnnotationProcessor.class.getClassLoader()
|
||||
.getResourceAsStream(annotationName.toString().replace('.', '/') + ".class")) {
|
||||
System.out.println("Index annotation: " + annotationName);
|
||||
indexer.index(annotationStream);
|
||||
additionalIndex.add(annotationName);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to index: " + beanClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,32 @@ package org.jboss.shamrock.deployment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BeanDeployment {
|
||||
|
||||
private final List<Class<?>> additionalBeans = new ArrayList<>();
|
||||
private final List<String> additionalBeans = new ArrayList<>();
|
||||
private final Map<String, byte[]> generatedBeans = new HashMap<>();
|
||||
|
||||
public void addAdditionalBean(Class<?> ... beanClass) {
|
||||
additionalBeans.addAll(Arrays.asList(beanClass));
|
||||
public void addAdditionalBean(Class<?>... beanClass) {
|
||||
additionalBeans.addAll(Arrays.stream(beanClass).map(Class::getName).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public List<Class<?>> getAdditionalBeans() {
|
||||
public void addAdditionalBean(String... beanClass) {
|
||||
additionalBeans.addAll(Arrays.stream(beanClass).collect(Collectors.toList()));
|
||||
}
|
||||
public void addGeneratedBean(String name, byte[] bean) {
|
||||
generatedBeans.put(name, bean);
|
||||
}
|
||||
|
||||
public List<String> getAdditionalBeans() {
|
||||
return additionalBeans;
|
||||
}
|
||||
|
||||
public Map<String, byte[]> getGeneratedBeans() {
|
||||
return generatedBeans;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -37,7 +37,6 @@ import org.jboss.jandex.Index;
|
||||
import org.jboss.jandex.Indexer;
|
||||
import org.jboss.jandex.MethodInfo;
|
||||
import org.jboss.jandex.Type;
|
||||
import org.jboss.protean.gizmo.BytecodeCreator;
|
||||
import org.jboss.protean.gizmo.CatchBlockCreator;
|
||||
import org.jboss.protean.gizmo.ClassCreator;
|
||||
import org.jboss.protean.gizmo.ExceptionTable;
|
||||
@@ -133,6 +132,7 @@ public class BuildTimeGenerator {
|
||||
|
||||
ArchiveContext context = new ArchiveContextImpl(new ApplicationArchiveImpl(appIndex, root, null), applicationArchives, config);
|
||||
ProcessorContextImpl processorContext = new ProcessorContextImpl();
|
||||
processorContext.addResource("META-INF/microprofile-config.properties");
|
||||
try {
|
||||
for (ResourceProcessor processor : processors) {
|
||||
try {
|
||||
@@ -169,6 +169,7 @@ public class BuildTimeGenerator {
|
||||
private final Set<String> resources = new HashSet<>();
|
||||
private final Set<String> resourceBundles = new HashSet<>();
|
||||
private final Set<String> runtimeInitializedClasses = new HashSet<>();
|
||||
private final Set<List<String>> proxyClasses = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public BytecodeRecorder addStaticInitTask(int priority) {
|
||||
@@ -256,6 +257,12 @@ public class BuildTimeGenerator {
|
||||
runtimeInitializedClasses.addAll(Arrays.asList(classes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProxyDefinition(String... proxyClasses) {
|
||||
this.proxyClasses.add(Arrays.asList(proxyClasses));
|
||||
}
|
||||
|
||||
|
||||
void writeMainClass() throws IOException {
|
||||
|
||||
Collections.sort(tasks);
|
||||
@@ -313,7 +320,7 @@ public class BuildTimeGenerator {
|
||||
|
||||
//TODO: at some point we are going to need to break this up, as if it get too big it will hit the method size limit
|
||||
|
||||
if(!runtimeInitializedClasses.isEmpty()) {
|
||||
if (!runtimeInitializedClasses.isEmpty()) {
|
||||
ExceptionTable tc = beforeAn.addTryCatch();
|
||||
ResultHandle array = beforeAn.newArray(Class.class, beforeAn.load(runtimeInitializedClasses.size()));
|
||||
int count = 0;
|
||||
@@ -330,6 +337,21 @@ public class BuildTimeGenerator {
|
||||
tc.complete();
|
||||
}
|
||||
|
||||
if (!proxyClasses.isEmpty()) {
|
||||
ResultHandle proxySupportClass = beforeAn.loadClass("com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry");
|
||||
ResultHandle proxySupport = beforeAn.invokeStaticMethod(ofMethod("org.graalvm.nativeimage.ImageSingletons", "lookup", Object.class, Class.class), proxySupportClass);
|
||||
for (List<String> proxy : proxyClasses) {
|
||||
ResultHandle array = beforeAn.newArray(Class.class, beforeAn.load(proxy.size()));
|
||||
int i = 0;
|
||||
for (String p : proxy) {
|
||||
ResultHandle clazz = beforeAn.invokeStaticMethod(ofMethod(Class.class, "forName", Class.class, String.class), beforeAn.load(p));
|
||||
beforeAn.writeArrayValue(array, beforeAn.load(i++), clazz);
|
||||
|
||||
}
|
||||
beforeAn.invokeInterfaceMethod(ofMethod("com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry", "addProxyClass", void.class, Class[].class), proxySupport, array);
|
||||
}
|
||||
}
|
||||
|
||||
for (String i : resources) {
|
||||
beforeAn.invokeStaticMethod(ofMethod(ResourceHelper.class, "registerResources", void.class, String.class), beforeAn.load(i));
|
||||
}
|
||||
|
||||
@@ -90,4 +90,6 @@ public interface ProcessorContext {
|
||||
void addResourceBundle(String bundle);
|
||||
|
||||
void addRuntimeInitializedClasses(String ... classes);
|
||||
|
||||
void addProxyDefinition(String ... proxyClasses);
|
||||
}
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
<groupId>io.reactivex.rxjava2</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<artifactId>shamrock-rest-client-deployment</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- test dependencies -->
|
||||
@@ -162,6 +167,7 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<cleanupServer>true</cleanupServer>
|
||||
<enableHttpUrlHandler>true</enableHttpUrlHandler>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.jboss.shamrock.example.rest;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.RestClientBuilder;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
|
||||
@Path("/client")
|
||||
public class ClientResource {
|
||||
|
||||
@Inject
|
||||
@RestClient
|
||||
private RestInterface restInterface;
|
||||
|
||||
@GET
|
||||
@Path("/manual")
|
||||
public String manual() throws Exception {
|
||||
|
||||
RestInterface iface = RestClientBuilder.newBuilder()
|
||||
.baseUrl(new URL("http", "localhost", 8080, "/rest"))
|
||||
.build(RestInterface.class);
|
||||
return iface.get();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/cdi")
|
||||
public String cdi() throws Exception {
|
||||
return restInterface.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.jboss.shamrock.example.rest;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
@Path("/test")
|
||||
public interface RestInterface {
|
||||
|
||||
@GET
|
||||
String get();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.jboss.shamrock.example.rest;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.ws.rs.GET;
|
||||
@@ -8,6 +10,8 @@ import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.RestClientBuilder;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Path("/test")
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jboss.shamrock.example.rest.RestInterface/mp-rest/url=http://localhost:8080/rest
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.jboss.shamrock.example.test;
|
||||
|
||||
import org.jboss.shamrock.junit.GraalTest;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(GraalTest.class)
|
||||
public class RestClientITCase extends RestClientTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.jboss.shamrock.example.test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import org.jboss.shamrock.junit.ShamrockTest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(ShamrockTest.class)
|
||||
public class RestClientTestCase {
|
||||
|
||||
@Test
|
||||
public void testMicroprofileClient() throws Exception {
|
||||
URL uri = new URL("http://localhost:8080/rest/client/manual");
|
||||
URLConnection connection = uri.openConnection();
|
||||
InputStream in = connection.getInputStream();
|
||||
byte[] buf = new byte[100];
|
||||
int r;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
while ((r = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, r);
|
||||
}
|
||||
Assert.assertEquals("TEST", new String(out.toByteArray()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMicroprofileClientCDIIntegration() throws Exception {
|
||||
URL uri = new URL("http://localhost:8080/rest/client/cdi");
|
||||
URLConnection connection = uri.openConnection();
|
||||
InputStream in = connection.getInputStream();
|
||||
byte[] buf = new byte[100];
|
||||
int r;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
while ((r = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, r);
|
||||
}
|
||||
Assert.assertEquals("TEST", new String(out.toByteArray()));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,37 @@
|
||||
package org.jboss.protean.gizmo;
|
||||
|
||||
import org.jboss.jandex.AnnotationInstance;
|
||||
import org.jboss.jandex.AnnotationValue;
|
||||
|
||||
public interface AnnotatedElement {
|
||||
|
||||
AnnotationCreator addAnnotation(String annotationType);
|
||||
|
||||
AnnotationCreator addAnnotation(Class<?> annotationType);
|
||||
|
||||
default void addAnnotation(AnnotationInstance annotation) {
|
||||
AnnotationCreator ac = addAnnotation(annotation.name().toString());
|
||||
for (AnnotationValue member : annotation.values()) {
|
||||
if (member.kind() == AnnotationValue.Kind.NESTED || member.kind() == AnnotationValue.Kind.ARRAY) {
|
||||
throw new RuntimeException("Not Yet Implemented: Cannot generate annotation " + annotation);
|
||||
} else if (member.kind() == AnnotationValue.Kind.BOOLEAN) {
|
||||
ac.addValue(member.name(), member.asBoolean());
|
||||
} else if (member.kind() == AnnotationValue.Kind.BYTE) {
|
||||
ac.addValue(member.name(), member.asByte());
|
||||
} else if (member.kind() == AnnotationValue.Kind.SHORT) {
|
||||
ac.addValue(member.name(), member.asShort());
|
||||
} else if (member.kind() == AnnotationValue.Kind.INTEGER) {
|
||||
ac.addValue(member.name(), member.asInt());
|
||||
} else if (member.kind() == AnnotationValue.Kind.LONG) {
|
||||
ac.addValue(member.name(), member.asLong());
|
||||
} else if (member.kind() == AnnotationValue.Kind.FLOAT) {
|
||||
ac.addValue(member.name(), member.asFloat());
|
||||
} else if (member.kind() == AnnotationValue.Kind.DOUBLE) {
|
||||
ac.addValue(member.name(), member.asDouble());
|
||||
} else if (member.kind() == AnnotationValue.Kind.STRING) {
|
||||
ac.addValue(member.name(), member.asString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jboss.jandex.AnnotationInstance;
|
||||
import org.jboss.jandex.AnnotationValue;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.util.List;
|
||||
/**
|
||||
* A class that builds the body of a method without needing to understand java bytecode.
|
||||
*/
|
||||
public interface MethodCreator extends MemberCreator<MethodCreator>, BytecodeCreator {
|
||||
public interface MethodCreator extends MemberCreator<MethodCreator>, BytecodeCreator, AnnotatedElement {
|
||||
|
||||
/**
|
||||
* Adds an exception to the method signature
|
||||
|
||||
@@ -4,8 +4,10 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
@@ -14,6 +16,7 @@ class MethodCreatorImpl extends BytecodeCreatorImpl implements MethodCreator {
|
||||
|
||||
private int modifiers = Opcodes.ACC_PUBLIC;
|
||||
private final List<String> exceptions = new ArrayList<>();
|
||||
private final List<AnnotationCreatorImpl> annotations = new ArrayList<>();
|
||||
|
||||
MethodCreatorImpl(MethodDescriptor methodDescriptor, String declaringClassName, ClassOutput classOutput, ClassCreator classCreator) {
|
||||
super(methodDescriptor, declaringClassName, classOutput, classCreator);
|
||||
@@ -62,6 +65,14 @@ class MethodCreatorImpl extends BytecodeCreatorImpl implements MethodCreator {
|
||||
int varCount = allocateLocalVariables(localVarCount);
|
||||
writeOperations(visitor);
|
||||
visitor.visitMaxs(0, varCount);
|
||||
|
||||
for(AnnotationCreatorImpl annotation : annotations) {
|
||||
AnnotationVisitor av = visitor.visitAnnotation(DescriptorUtils.extToInt(annotation.getAnnotationType()), true);
|
||||
for(Map.Entry<String, Object> e : annotation.getValues().entrySet()) {
|
||||
av.visit(e.getKey(), e.getValue());
|
||||
}
|
||||
av.visitEnd();
|
||||
}
|
||||
visitor.visitEnd();
|
||||
}
|
||||
|
||||
@@ -69,4 +80,16 @@ class MethodCreatorImpl extends BytecodeCreatorImpl implements MethodCreator {
|
||||
public String toString() {
|
||||
return "MethodCreatorImpl [declaringClassName=" + declaringClassName + ", methodDescriptor=" + methodDescriptor + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationCreator addAnnotation(String annotationType) {
|
||||
AnnotationCreatorImpl ac = new AnnotationCreatorImpl(annotationType);
|
||||
annotations.add(ac);
|
||||
return ac;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationCreator addAnnotation(Class<?> annotationType) {
|
||||
return addAnnotation(annotationType.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<artifactId>shamrock-jaxrs-runtime</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.graalvm</groupId>
|
||||
<artifactId>graal-annotations</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ public class NativeImageMojo extends AbstractMojo {
|
||||
@Parameter(defaultValue = "${native-image.new-server}")
|
||||
private boolean cleanupServer;
|
||||
|
||||
@Parameter
|
||||
private boolean enableHttpUrlHandler;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||
@@ -75,6 +77,8 @@ public class NativeImageMojo extends AbstractMojo {
|
||||
}
|
||||
command.add("-jar");
|
||||
command.add(finalName + "-runner.jar");
|
||||
//https://github.com/oracle/graal/issues/660
|
||||
command.add("-J-Djava.util.concurrent.ForkJoinPool.common.parallelism=1");
|
||||
if (reportErrorsAtRuntime) {
|
||||
command.add("-H:+ReportUnsupportedElementsAtRuntime");
|
||||
}
|
||||
@@ -87,6 +91,9 @@ public class NativeImageMojo extends AbstractMojo {
|
||||
command.add("-J-Djava.compiler=NONE");
|
||||
command.add("-J-Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=y");
|
||||
}
|
||||
if(enableHttpUrlHandler) {
|
||||
command.add("-H:EnableURLProtocols=http");
|
||||
}
|
||||
//command.add("-H:+AllowVMInspection");
|
||||
System.out.println(command);
|
||||
Process process = Runtime.getRuntime().exec(command.toArray(new String[0]), null, outputDirectory);
|
||||
|
||||
22
pom.xml
22
pom.xml
@@ -56,6 +56,7 @@
|
||||
<jboss-transaction-spi.version>7.6.0.Final</jboss-transaction-spi.version>
|
||||
<javax.persistence-api.version>2.2</javax.persistence-api.version>
|
||||
<rxjava.version>2.1.12</rxjava.version>
|
||||
<microprofile-rest-client-api.version>1.0</microprofile-rest-client-api.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
@@ -76,6 +77,7 @@
|
||||
<module>bean-validation</module>
|
||||
<module>transactions</module>
|
||||
<module>agroal</module>
|
||||
<module>rest-client</module>
|
||||
</modules>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
@@ -211,6 +213,16 @@
|
||||
<artifactId>shamrock-junit</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<artifactId>shamrock-rest-client-deployment</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<artifactId>shamrock-rest-client-runtime</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<artifactId>shamrock-shared-library-example</artifactId>
|
||||
@@ -422,6 +434,11 @@
|
||||
<artifactId>microprofile-config-api</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.microprofile.rest.client</groupId>
|
||||
<artifactId>microprofile-rest-client-api</artifactId>
|
||||
<version>${microprofile-rest-client-api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fakereplace</groupId>
|
||||
<artifactId>fakereplace</artifactId>
|
||||
@@ -490,6 +507,11 @@
|
||||
<artifactId>resteasy-cdi</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
|
||||
27
rest-client/deployment/pom.xml
Normal file
27
rest-client/deployment/pom.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>shamrock-rest-client</artifactId>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<version>1.0.0.Alpha1-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>shamrock-rest-client-deployment</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<artifactId>shamrock-core-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<artifactId>shamrock-rest-client-runtime</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,141 @@
|
||||
package org.jboss.shamrock.restclient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.inject.Default;
|
||||
import javax.enterprise.inject.Produces;
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.client.ClientRequestFilter;
|
||||
import javax.ws.rs.client.ClientResponseFilter;
|
||||
|
||||
import org.apache.commons.logging.impl.Jdk14Logger;
|
||||
import org.apache.commons.logging.impl.LogFactoryImpl;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.jandex.AnnotationInstance;
|
||||
import org.jboss.jandex.AnnotationTarget;
|
||||
import org.jboss.jandex.ClassInfo;
|
||||
import org.jboss.jandex.DotName;
|
||||
import org.jboss.protean.gizmo.ClassCreator;
|
||||
import org.jboss.protean.gizmo.ClassOutput;
|
||||
import org.jboss.protean.gizmo.MethodCreator;
|
||||
import org.jboss.protean.gizmo.MethodDescriptor;
|
||||
import org.jboss.protean.gizmo.ResultHandle;
|
||||
import org.jboss.resteasy.client.jaxrs.internal.proxy.ResteasyClientProxy;
|
||||
import org.jboss.resteasy.spi.ResteasyConfiguration;
|
||||
import org.jboss.shamrock.deployment.ArchiveContext;
|
||||
import org.jboss.shamrock.deployment.BeanDeployment;
|
||||
import org.jboss.shamrock.deployment.ProcessorContext;
|
||||
import org.jboss.shamrock.deployment.ResourceProcessor;
|
||||
import org.jboss.shamrock.deployment.ShamrockConfig;
|
||||
import org.jboss.shamrock.restclient.runtime.DefaultResponseExceptionMapper;
|
||||
import org.jboss.shamrock.restclient.runtime.RestClientBase;
|
||||
import org.jboss.shamrock.restclient.runtime.RestClientProxy;
|
||||
|
||||
class RestClientProcessor implements ResourceProcessor {
|
||||
|
||||
private static final Logger log = Logger.getLogger(RestClientProxy.class.getName());
|
||||
|
||||
private static final DotName REGISTER_REST_CLIENT = DotName.createSimple(RegisterRestClient.class.getName());
|
||||
@Inject
|
||||
private BeanDeployment beanDeployment;
|
||||
|
||||
@Inject
|
||||
private ShamrockConfig config;
|
||||
|
||||
private static final DotName[] CLIENT_ANNOTATIONS = {
|
||||
DotName.createSimple("javax.ws.rs.GET"),
|
||||
DotName.createSimple("javax.ws.rs.HEAD"),
|
||||
DotName.createSimple("javax.ws.rs.DELETE"),
|
||||
DotName.createSimple("javax.ws.rs.OPTIONS"),
|
||||
DotName.createSimple("javax.ws.rs.PATCH"),
|
||||
DotName.createSimple("javax.ws.rs.POST"),
|
||||
DotName.createSimple("javax.ws.rs.PUT"),
|
||||
DotName.createSimple("javax.ws.rs.PUT"),
|
||||
DotName.createSimple(RegisterRestClient.class.getName()),
|
||||
DotName.createSimple(Path.class.getName())
|
||||
};
|
||||
|
||||
@Override
|
||||
public void process(ArchiveContext archiveContext, ProcessorContext processorContext) throws Exception {
|
||||
processorContext.addReflectiveClass(false, false,
|
||||
DefaultResponseExceptionMapper.class.getName(),
|
||||
LogFactoryImpl.class.getName(),
|
||||
Jdk14Logger.class.getName());
|
||||
processorContext.addReflectiveClass(false, false, ClientRequestFilter[].class.getName());
|
||||
processorContext.addReflectiveClass(false, false, ClientResponseFilter[].class.getName());
|
||||
beanDeployment.addAdditionalBean(RestClient.class);
|
||||
processorContext.addResource("META-INF/services/javax.ws.rs.ext.Providers");
|
||||
//TODO: fix this, we don't want to just add all the providers
|
||||
processorContext.addReflectiveClass(false, false, "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
|
||||
processorContext.addReflectiveClass(false, false, "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
|
||||
processorContext.addProxyDefinition(ResteasyConfiguration.class.getName());
|
||||
Map<DotName, ClassInfo> interfaces = new HashMap<>();
|
||||
for (DotName type : CLIENT_ANNOTATIONS) {
|
||||
for (AnnotationInstance annotation : archiveContext.getCombinedIndex().getAnnotations(type)) {
|
||||
AnnotationTarget target = annotation.target();
|
||||
ClassInfo theInfo;
|
||||
if (target.kind() == AnnotationTarget.Kind.CLASS) {
|
||||
theInfo = target.asClass();
|
||||
} else if (target.kind() == AnnotationTarget.Kind.METHOD) {
|
||||
theInfo = target.asMethod().declaringClass();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (!Modifier.isInterface(theInfo.flags())) {
|
||||
continue;
|
||||
}
|
||||
interfaces.put(theInfo.name(), theInfo);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<DotName, ClassInfo> entry : interfaces.entrySet()) {
|
||||
String iName = entry.getKey().toString();
|
||||
processorContext.addProxyDefinition(iName, ResteasyClientProxy.class.getName());
|
||||
processorContext.addProxyDefinition(iName, RestClientProxy.class.getName());
|
||||
processorContext.addReflectiveClass(true, false, iName);
|
||||
|
||||
//now generate CDI beans
|
||||
//TODO: do we need to check if CDI is enabled? Are we just assuming it always is?
|
||||
String className = iName + "$$RestClientProxy";
|
||||
AtomicReference<byte[]> bytes= new AtomicReference<>();
|
||||
try (ClassCreator creator = new ClassCreator(new ClassOutput() {
|
||||
@Override
|
||||
public void write(String name, byte[] data) {
|
||||
try {
|
||||
bytes.set(data);
|
||||
processorContext.addGeneratedClass(true, name, data);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}, className, null, RestClientBase.class.getName())) {
|
||||
|
||||
creator.addAnnotation(ApplicationScoped.class);
|
||||
MethodCreator producer = creator.getMethodCreator("producerMethod", iName);
|
||||
producer.addAnnotation(Produces.class);
|
||||
producer.addAnnotation(RestClient.class);
|
||||
|
||||
ResultHandle ret = producer.invokeVirtualMethod(MethodDescriptor.ofMethod(RestClientBase.class, "create", Object.class), producer.getThis());
|
||||
producer.returnValue(ret);
|
||||
|
||||
MethodCreator ctor = creator.getMethodCreator(MethodDescriptor.ofConstructor(className));
|
||||
ctor.invokeSpecialMethod(MethodDescriptor.ofConstructor(RestClientBase.class, Class.class), ctor.getThis(), ctor.loadClass(iName));
|
||||
ctor.returnValue(null);
|
||||
}
|
||||
beanDeployment.addGeneratedBean(className, bytes.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.jboss.shamrock.restclient;
|
||||
|
||||
import org.jboss.shamrock.deployment.SetupContext;
|
||||
import org.jboss.shamrock.deployment.ShamrockSetup;
|
||||
|
||||
public class RestClientSetup implements ShamrockSetup {
|
||||
@Override
|
||||
public void setup(SetupContext context) {
|
||||
context.addResourceProcessor(new RestClientProcessor());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jboss.shamrock.restclient.RestClientSetup
|
||||
18
rest-client/pom.xml
Normal file
18
rest-client/pom.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>shamrock-parent</artifactId>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<version>1.0.0.Alpha1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>shamrock-rest-client</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>deployment</module>
|
||||
<module>runtime</module>
|
||||
</modules>
|
||||
</project>
|
||||
45
rest-client/runtime/pom.xml
Normal file
45
rest-client/runtime/pom.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>shamrock-rest-client</artifactId>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<version>1.0.0.Alpha1-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>shamrock-rest-client-runtime</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.shamrock</groupId>
|
||||
<artifactId>shamrock-core-runtime</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.microprofile.rest.client</groupId>
|
||||
<artifactId>microprofile-rest-client-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.graalvm</groupId>
|
||||
<artifactId>graal-annotations</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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 org.eclipse.microprofile.rest.client.RestClientBuilder;
|
||||
import org.eclipse.microprofile.rest.client.spi.RestClientBuilderResolver;
|
||||
|
||||
/**
|
||||
* Created by hbraun on 22.01.18.
|
||||
*/
|
||||
public class BuilderResolver extends RestClientBuilderResolver {
|
||||
@Override
|
||||
public RestClientBuilder newBuilder() {
|
||||
return new RestClientBuilderImpl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* 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.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.RuntimeType;
|
||||
import javax.ws.rs.core.Configuration;
|
||||
import javax.ws.rs.core.Feature;
|
||||
|
||||
/**
|
||||
* Created by hbraun on 22.01.18.
|
||||
*/
|
||||
class ConfigurationWrapper implements Configuration {
|
||||
|
||||
public ConfigurationWrapper(Configuration delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeType getRuntimeType() {
|
||||
return delegate.getRuntimeType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getProperties() {
|
||||
return delegate.getProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
return delegate.getProperty(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getPropertyNames() {
|
||||
return delegate.getPropertyNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(Feature feature) {
|
||||
return delegate.isEnabled(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(Class<? extends Feature> featureClass) {
|
||||
return delegate.isEnabled(featureClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(Object component) {
|
||||
return delegate.isRegistered(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(Class<?> componentClass) {
|
||||
return delegate.isRegistered(componentClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Class<?>, Integer> getContracts(Class<?> componentClass) {
|
||||
Map<Class<?>, Integer> contracts = new HashMap<>();
|
||||
contracts.putAll(getLocalContracts(componentClass));
|
||||
contracts.putAll(delegate.getContracts(componentClass));
|
||||
return contracts;
|
||||
}
|
||||
|
||||
private Map<Class<?>, ? extends Integer> getLocalContracts(Class<?> componentClass) {
|
||||
if (localClassContracts.containsKey(componentClass)) {
|
||||
return localClassContracts.get(componentClass);
|
||||
} else {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> getClasses() {
|
||||
return delegate.getClasses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> getInstances() {
|
||||
return delegate.getInstances();
|
||||
}
|
||||
|
||||
void registerLocalContract(Class<?> provider, Map<Class<?>, Integer> contracts) {
|
||||
localClassContracts.put(provider, contracts);
|
||||
}
|
||||
|
||||
protected Map<Class<?>, Map<Class<?>, Integer>> localClassContracts = new HashMap<>();
|
||||
|
||||
private final Configuration delegate;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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 javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
|
||||
|
||||
/**
|
||||
* Created by hbraun on 17.01.18.
|
||||
*/
|
||||
public class DefaultResponseExceptionMapper implements ResponseExceptionMapper {
|
||||
|
||||
@Override
|
||||
public Throwable toThrowable(Response response) {
|
||||
return new WebApplicationException("Unknown error, status code " + response.getStatus(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handles(int status, MultivaluedMap headers) {
|
||||
return status >= 400;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.client.ClientRequestContext;
|
||||
import javax.ws.rs.client.ClientResponseContext;
|
||||
import javax.ws.rs.client.ClientResponseFilter;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
|
||||
|
||||
/**
|
||||
* Created by hbraun on 22.01.18.
|
||||
*/
|
||||
class ExceptionMapping implements ClientResponseFilter {
|
||||
|
||||
ExceptionMapping(Set<Object> instances) {
|
||||
this.instances = instances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
|
||||
|
||||
Response response = new PartialResponse(responseContext);
|
||||
|
||||
Map<ResponseExceptionMapper, Integer> mappers = new HashMap<>();
|
||||
for (Object o : instances) {
|
||||
if (o instanceof ResponseExceptionMapper) {
|
||||
ResponseExceptionMapper candiate = (ResponseExceptionMapper) o;
|
||||
if (candiate.handles(response.getStatus(), response.getHeaders())) {
|
||||
mappers.put(candiate, candiate.getPriority());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mappers.size() > 0) {
|
||||
Map<Optional<Throwable>, Integer> errors = new HashMap<>();
|
||||
|
||||
mappers.forEach((m, i) -> {
|
||||
Optional<Throwable> t = Optional.ofNullable(m.toThrowable(response));
|
||||
errors.put(t, i);
|
||||
});
|
||||
|
||||
Optional<Throwable> prioritised = Optional.empty();
|
||||
for (Optional<Throwable> throwable : errors.keySet()) {
|
||||
if (throwable.isPresent()) {
|
||||
if (!prioritised.isPresent()) {
|
||||
prioritised = throwable;
|
||||
} else if (errors.get(throwable) < errors.get(prioritised)) {
|
||||
prioritised = throwable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prioritised.isPresent()) { // strange rule from the spec
|
||||
throw (WebApplicationException) prioritised.get();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Set<Object> instances;
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* 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.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.URI;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.client.ClientResponseContext;
|
||||
import javax.ws.rs.core.EntityTag;
|
||||
import javax.ws.rs.core.GenericType;
|
||||
import javax.ws.rs.core.Link;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.NewCookie;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||
|
||||
|
||||
/**
|
||||
* Created by hbraun on 22.01.18.
|
||||
*/
|
||||
public class PartialResponse extends Response implements Serializable {
|
||||
|
||||
PartialResponse(ClientResponseContext responseContext) {
|
||||
this.responseContext = responseContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatus() {
|
||||
return responseContext.getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatusType getStatusInfo() {
|
||||
return responseContext.getStatusInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEntity() {
|
||||
throw notSupported();
|
||||
}
|
||||
|
||||
private RuntimeException notSupported() {
|
||||
RuntimeException ex = new RuntimeException("method call not supported");
|
||||
ex.printStackTrace();
|
||||
return ex;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T readEntity(Class<T> entityType) {
|
||||
|
||||
if (entityType.isAssignableFrom(String.class)) {
|
||||
return (T) readStringEntity(responseContext.getEntityStream());
|
||||
} else {
|
||||
throw notSupported();
|
||||
}
|
||||
}
|
||||
|
||||
public static String readStringEntity(InputStream input) {
|
||||
try {
|
||||
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) {
|
||||
return buffer.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new WebApplicationException("Failed to read entity", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T readEntity(GenericType<T> entityType) {
|
||||
throw notSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T readEntity(Class<T> entityType, Annotation[] annotations) {
|
||||
throw notSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T readEntity(GenericType<T> entityType, Annotation[] annotations) {
|
||||
throw notSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEntity() {
|
||||
return responseContext.hasEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bufferEntity() {
|
||||
throw new RuntimeException("method call not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
responseContext.getEntityStream().close();
|
||||
} catch (Throwable e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType getMediaType() {
|
||||
return responseContext.getMediaType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLanguage() {
|
||||
return responseContext.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return responseContext.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAllowedMethods() {
|
||||
return responseContext.getAllowedMethods();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, NewCookie> getCookies() {
|
||||
return responseContext.getCookies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityTag getEntityTag() {
|
||||
return responseContext.getEntityTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDate() {
|
||||
return responseContext.getDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastModified() {
|
||||
return responseContext.getLastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getLocation() {
|
||||
return responseContext.getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Link> getLinks() {
|
||||
return responseContext.getLinks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLink(String relation) {
|
||||
return responseContext.hasLink(relation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Link getLink(String relation) {
|
||||
return responseContext.getLink(relation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Link.Builder getLinkBuilder(String relation) {
|
||||
throw new RuntimeException("method call not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultivaluedMap<String, Object> getMetadata() {
|
||||
MultivaluedMap<String, Object> metaData = new MultivaluedMapImpl<String, Object>();
|
||||
// TODO
|
||||
return metaData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultivaluedMap<String, String> getStringHeaders() {
|
||||
return responseContext.getHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeaderString(String name) {
|
||||
return responseContext.getHeaderString(name);
|
||||
}
|
||||
|
||||
private final transient ClientResponseContext responseContext;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.jboss.shamrock.restclient.runtime;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
import org.eclipse.microprofile.config.ConfigProvider;
|
||||
import org.eclipse.microprofile.rest.client.RestClientBuilder;
|
||||
|
||||
public class RestClientBase {
|
||||
|
||||
public static final String REST_URL_FORMAT = "%s/mp-rest/url";
|
||||
|
||||
private final Class<?> proxyType;
|
||||
|
||||
private final Config config;
|
||||
|
||||
public RestClientBase(Class<?> proxyType) {
|
||||
this.proxyType = proxyType;
|
||||
this.config = ConfigProvider.getConfig();
|
||||
}
|
||||
|
||||
public Object create() {
|
||||
RestClientBuilder builder = RestClientBuilder.newBuilder();
|
||||
String baseUrl = getBaseUrl();
|
||||
try {
|
||||
return builder.baseUrl(new URL(baseUrl)).build(proxyType);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException("The value of URL was invalid " + baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private String getBaseUrl() {
|
||||
String property = String.format(REST_URL_FORMAT, proxyType.getName());
|
||||
return config.getValue(property, String.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
/**
|
||||
* Copyright 2015-2017 Red Hat, Inc, and individual contributors.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.io.IOException;
|
||||
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;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLContextSpi;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.ws.rs.HttpMethod;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Priorities;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.core.Configuration;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.ext.ParamConverterProvider;
|
||||
|
||||
import org.eclipse.microprofile.config.Config;
|
||||
import org.eclipse.microprofile.config.ConfigProvider;
|
||||
import org.eclipse.microprofile.rest.client.RestClientBuilder;
|
||||
import org.eclipse.microprofile.rest.client.RestClientDefinitionException;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
|
||||
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
import org.jboss.resteasy.specimpl.ResteasyUriBuilder;
|
||||
|
||||
|
||||
/**
|
||||
* Created by hbraun on 15.01.18.
|
||||
*/
|
||||
class RestClientBuilderImpl implements RestClientBuilder {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(RestClientBuilderImpl.class);
|
||||
|
||||
private static final String RESTEASY_PROPERTY_PREFIX = "resteasy.";
|
||||
|
||||
private static final String DEFAULT_MAPPER_PROP = "microprofile.rest.client.disable.default.mapper";
|
||||
|
||||
RestClientBuilderImpl() {
|
||||
ClientBuilder availableBuilder = ClientBuilder.newBuilder();
|
||||
|
||||
if (availableBuilder instanceof ResteasyClientBuilder) {
|
||||
this.builderDelegate = (ResteasyClientBuilder) availableBuilder;
|
||||
this.configurationWrapper = new ConfigurationWrapper(this.builderDelegate.getConfiguration());
|
||||
this.config = ConfigProvider.getConfig();
|
||||
} else {
|
||||
throw new IllegalStateException("Incompatible client builder found " + availableBuilder.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public Configuration getConfigurationWrapper() {
|
||||
return this.configurationWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder baseUrl(URL url) {
|
||||
try {
|
||||
this.baseURI = url.toURI();
|
||||
return this;
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T build(Class<T> aClass) throws IllegalStateException, RestClientDefinitionException {
|
||||
|
||||
// Interface validity
|
||||
verifyInterface(aClass);
|
||||
|
||||
// Provider annotations
|
||||
Annotation[] providers = aClass.getAnnotations();
|
||||
|
||||
for (Annotation provider : providers) {
|
||||
if(provider instanceof RegisterProvider) {
|
||||
RegisterProvider p = (RegisterProvider) provider;
|
||||
register(p.value(), p.priority());
|
||||
}
|
||||
}
|
||||
|
||||
// Default exception mapper
|
||||
if (!isMapperDisabled()) {
|
||||
register(DefaultResponseExceptionMapper.class);
|
||||
}
|
||||
|
||||
this.builderDelegate.register(new ExceptionMapping(localProviderInstances), 1);
|
||||
|
||||
ClassLoader classLoader = aClass.getClassLoader();
|
||||
|
||||
List<String> noProxyHosts = Arrays.asList(
|
||||
System.getProperty("http.nonProxyHosts", "localhost|127.*|[::1]").split("|"));
|
||||
String proxyHost = System.getProperty("http.proxyHost");
|
||||
|
||||
T actualClient;
|
||||
ResteasyClient client;
|
||||
//TODO: Substrate does not support SSL yet
|
||||
this.builderDelegate.sslContext(new SSLContext(new SSLContextSpi() {
|
||||
@Override
|
||||
protected void engineInit(KeyManager[] keyManagers, TrustManager[] trustManagers, SecureRandom secureRandom) throws KeyManagementException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSLSocketFactory engineGetSocketFactory() {
|
||||
return new SSLSocketFactory() {
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String s, int i) throws IOException, UnknownHostException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSLServerSocketFactory engineGetServerSocketFactory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSLEngine engineCreateSSLEngine() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSLEngine engineCreateSSLEngine(String s, int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSLSessionContext engineGetServerSessionContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSLSessionContext engineGetClientSessionContext() {
|
||||
return null;
|
||||
}
|
||||
}, new Provider("Dummy", 1, "Dummy") {
|
||||
@Override
|
||||
public String getName() {
|
||||
return super.getName();
|
||||
}
|
||||
}, "BOGUS") {
|
||||
|
||||
});
|
||||
if (proxyHost != null && !noProxyHosts.contains(this.baseURI.getHost())) {
|
||||
// Use proxy, if defined
|
||||
client = this.builderDelegate.defaultProxy(
|
||||
proxyHost,
|
||||
Integer.parseInt(System.getProperty("http.proxyPort", "80")))
|
||||
.build();
|
||||
} else {
|
||||
client = this.builderDelegate.build();
|
||||
}
|
||||
|
||||
actualClient = client.target(this.baseURI)
|
||||
.proxyBuilder(aClass)
|
||||
.classloader(classLoader)
|
||||
.defaultConsumes(MediaType.TEXT_PLAIN)
|
||||
.defaultProduces(MediaType.TEXT_PLAIN).build();
|
||||
|
||||
Class<?>[] interfaces = new Class<?>[2];
|
||||
interfaces[0] = aClass;
|
||||
interfaces[1] = RestClientProxy.class;
|
||||
|
||||
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() {
|
||||
boolean disabled = false;
|
||||
Optional<Boolean> defaultMapperProp = this.config.getOptionalValue(DEFAULT_MAPPER_PROP, Boolean.class);
|
||||
|
||||
// disabled through config api
|
||||
if (defaultMapperProp.isPresent() && defaultMapperProp.get().equals(Boolean.TRUE)) {
|
||||
disabled = true;
|
||||
} else if (!defaultMapperProp.isPresent()) {
|
||||
|
||||
// disabled through jaxrs property
|
||||
try {
|
||||
Object property = this.builderDelegate.getConfiguration().getProperty(DEFAULT_MAPPER_PROP);
|
||||
if (property != null) {
|
||||
disabled = (Boolean) property;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// ignore cast exception
|
||||
}
|
||||
}
|
||||
return disabled;
|
||||
}
|
||||
|
||||
private <T> void verifyInterface(Class<T> typeDef) {
|
||||
|
||||
Method[] methods = typeDef.getMethods();
|
||||
|
||||
// multiple verbs
|
||||
for (Method method : methods) {
|
||||
boolean hasHttpMethod = false;
|
||||
for (Annotation annotation : method.getAnnotations()) {
|
||||
boolean isHttpMethod = (annotation.annotationType().getAnnotation(HttpMethod.class) != null);
|
||||
if (!hasHttpMethod && isHttpMethod) {
|
||||
hasHttpMethod = true;
|
||||
} else if (hasHttpMethod && isHttpMethod) {
|
||||
throw new RestClientDefinitionException("Ambiguous @Httpmethod defintion on type " + typeDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// invalid parameter
|
||||
Path classPathAnno = typeDef.getAnnotation(Path.class);
|
||||
|
||||
final Set<String> classLevelVariables = new HashSet<>();
|
||||
ResteasyUriBuilder classTemplate = null;
|
||||
if (classPathAnno != null) {
|
||||
classTemplate = (ResteasyUriBuilder) UriBuilder.fromUri(classPathAnno.value());
|
||||
classLevelVariables.addAll(classTemplate.getPathParamNamesInDeclarationOrder());
|
||||
}
|
||||
ResteasyUriBuilder template;
|
||||
for (Method method : methods) {
|
||||
|
||||
Path methodPathAnno = method.getAnnotation(Path.class);
|
||||
if (methodPathAnno != null) {
|
||||
template = classPathAnno == null ? (ResteasyUriBuilder) UriBuilder.fromUri(methodPathAnno.value())
|
||||
: (ResteasyUriBuilder) UriBuilder.fromUri(classPathAnno.value() + "/" + methodPathAnno.value());
|
||||
} else {
|
||||
template = classTemplate;
|
||||
}
|
||||
if (template == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// it's not executed, so this can be anything - but a hostname needs to present
|
||||
template.host("localhost");
|
||||
|
||||
Set<String> allVariables = new HashSet<>(template.getPathParamNamesInDeclarationOrder());
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
for (Parameter p : method.getParameters()) {
|
||||
PathParam pathParam = p.getAnnotation(PathParam.class);
|
||||
if (pathParam != null) {
|
||||
paramMap.put(pathParam.value(), "foobar");
|
||||
}
|
||||
}
|
||||
|
||||
if (allVariables.size() != paramMap.size()) {
|
||||
throw new RestClientDefinitionException("Parameters and variables don't match on " + typeDef + "::" + method.getName());
|
||||
}
|
||||
|
||||
try {
|
||||
template.resolveTemplates(paramMap, false).build();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new RestClientDefinitionException("Parameter names don't match variable names on " + typeDef + "::" + method.getName(), ex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Configuration getConfiguration() {
|
||||
return getConfigurationWrapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder property(String name, Object value) {
|
||||
if (name.startsWith(RESTEASY_PROPERTY_PREFIX)) {
|
||||
// Allows to configure some of the ResteasyClientBuilder delegate properties
|
||||
String builderMethodName = name.substring(RESTEASY_PROPERTY_PREFIX.length());
|
||||
try {
|
||||
Method builderMethod = ResteasyClientBuilder.class.getMethod(builderMethodName, unwrapPrimitiveType(value));
|
||||
builderMethod.invoke(builderDelegate, value);
|
||||
} catch (NoSuchMethodException e) {
|
||||
LOGGER.warnf("ResteasyClientBuilder method %s not found", builderMethodName);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
LOGGER.errorf(e, "Unable to invoke ResteasyClientBuilder method %s", builderMethodName);
|
||||
}
|
||||
}
|
||||
this.builderDelegate.property(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static Class<?> unwrapPrimitiveType(Object value) {
|
||||
if (value instanceof Integer) {
|
||||
return int.class;
|
||||
} else if (value instanceof Long) {
|
||||
return long.class;
|
||||
} else if (value instanceof Boolean) {
|
||||
return boolean.class;
|
||||
}
|
||||
return value.getClass();
|
||||
}
|
||||
|
||||
private static Object newInstanceOf(Class<?> clazz) {
|
||||
try {
|
||||
return clazz.newInstance();
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Failed to register " + clazz, t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder register(Class<?> aClass) {
|
||||
this.register(newInstanceOf(aClass));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder register(Class<?> aClass, int i) {
|
||||
|
||||
this.register(newInstanceOf(aClass), i);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder register(Class<?> aClass, Class<?>[] classes) {
|
||||
this.register(newInstanceOf(aClass), classes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder register(Class<?> aClass, Map<Class<?>, Integer> map) {
|
||||
this.register(newInstanceOf(aClass), map);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder register(Object o) {
|
||||
if (o instanceof ResponseExceptionMapper) {
|
||||
ResponseExceptionMapper mapper = (ResponseExceptionMapper) o;
|
||||
register(mapper, mapper.getPriority());
|
||||
} else if (o instanceof ParamConverterProvider) {
|
||||
register(o, Priorities.USER);
|
||||
} else {
|
||||
this.builderDelegate.register(o);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder register(Object o, int i) {
|
||||
if (o instanceof ResponseExceptionMapper) {
|
||||
|
||||
// local
|
||||
ResponseExceptionMapper mapper = (ResponseExceptionMapper) o;
|
||||
HashMap<Class<?>, Integer> contracts = new HashMap<>();
|
||||
contracts.put(ResponseExceptionMapper.class, i);
|
||||
registerLocalProviderInstance(mapper, contracts);
|
||||
|
||||
// delegate
|
||||
this.builderDelegate.register(mapper, i);
|
||||
|
||||
} else if (o instanceof ParamConverterProvider) {
|
||||
|
||||
// local
|
||||
ParamConverterProvider converter = (ParamConverterProvider) o;
|
||||
HashMap<Class<?>, Integer> contracts = new HashMap<>();
|
||||
contracts.put(ParamConverterProvider.class, i);
|
||||
registerLocalProviderInstance(converter, contracts);
|
||||
|
||||
// delegate
|
||||
this.builderDelegate.register(converter, i);
|
||||
|
||||
} else {
|
||||
this.builderDelegate.register(o, i);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder register(Object o, Class<?>[] classes) {
|
||||
|
||||
// local
|
||||
for (Class<?> aClass : classes) {
|
||||
if (aClass.isAssignableFrom(ResponseExceptionMapper.class)) {
|
||||
register(o);
|
||||
}
|
||||
}
|
||||
|
||||
// other
|
||||
this.builderDelegate.register(o, classes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestClientBuilder register(Object o, Map<Class<?>, Integer> map) {
|
||||
|
||||
|
||||
if (o instanceof ResponseExceptionMapper) {
|
||||
|
||||
//local
|
||||
ResponseExceptionMapper mapper = (ResponseExceptionMapper) o;
|
||||
HashMap<Class<?>, Integer> contracts = new HashMap<>();
|
||||
contracts.put(ResponseExceptionMapper.class, map.get(ResponseExceptionMapper.class));
|
||||
registerLocalProviderInstance(mapper, contracts);
|
||||
|
||||
// other
|
||||
this.builderDelegate.register(o, map);
|
||||
|
||||
} else {
|
||||
this.builderDelegate.register(o, map);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set<Object> getLocalProviderInstances() {
|
||||
return localProviderInstances;
|
||||
}
|
||||
|
||||
public void registerLocalProviderInstance(Object provider, Map<Class<?>, Integer> contracts) {
|
||||
for (Object registered : getLocalProviderInstances()) {
|
||||
if (registered == provider) {
|
||||
System.out.println("Provider already registered " + provider.getClass().getName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
localProviderInstances.add(provider);
|
||||
configurationWrapper.registerLocalContract(provider.getClass(), contracts);
|
||||
}
|
||||
|
||||
private final ResteasyClientBuilder builderDelegate;
|
||||
|
||||
private final ConfigurationWrapper configurationWrapper;
|
||||
|
||||
private final Config config;
|
||||
|
||||
private URI baseURI;
|
||||
|
||||
private Set<Object> localProviderInstances = new HashSet<Object>();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright 2018 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 javax.ws.rs.client.Client;
|
||||
|
||||
/**
|
||||
* This interface is implemented by every proxy created by {@link io.smallrye.restclient.RestClientBuilderImpl}.
|
||||
*
|
||||
* @author Martin Kouba
|
||||
*/
|
||||
public interface RestClientProxy {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the underlying {@link Client} instance
|
||||
*/
|
||||
Client getClient();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.jboss.shamrock.restclient.runtime.graal;
|
||||
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
|
||||
import com.oracle.svm.core.annotate.Substitute;
|
||||
import com.oracle.svm.core.annotate.TargetClass;
|
||||
|
||||
@TargetClass(ClientBuilder.class)
|
||||
final class ClientBuilderReplacement {
|
||||
|
||||
@Substitute
|
||||
public static ClientBuilder newBuilder() {
|
||||
return new ResteasyClientBuilder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.jboss.shamrock.restclient.runtime.graal;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.spi.RestClientBuilderResolver;
|
||||
import org.jboss.shamrock.restclient.runtime.BuilderResolver;
|
||||
|
||||
import com.oracle.svm.core.annotate.Substitute;
|
||||
import com.oracle.svm.core.annotate.TargetClass;
|
||||
|
||||
@TargetClass(RestClientBuilderResolver.class)
|
||||
final class RestClientBuilderResolverReplacement {
|
||||
|
||||
@Substitute
|
||||
private static RestClientBuilderResolver loadSpi(ClassLoader cl) {
|
||||
return new BuilderResolver();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jboss.shamrock.restclient.runtime.BuilderResolver
|
||||
@@ -38,8 +38,11 @@ public class WeldAnnotationProcessor implements ResourceProcessor {
|
||||
template.addClass(init, recorder.classProxy(name));
|
||||
processorContext.addReflectiveClass(true, true, name);
|
||||
}
|
||||
for (Class<?> clazz : beanDeployment.getAdditionalBeans()) {
|
||||
template.addClass(init, clazz);
|
||||
for (String clazz : beanDeployment.getAdditionalBeans()) {
|
||||
template.addClass(init, recorder.classProxy(clazz));
|
||||
}
|
||||
for (String clazz : beanDeployment.getGeneratedBeans().keySet()) {
|
||||
template.addClass(init, recorder.classProxy(clazz));
|
||||
}
|
||||
SeContainer weld = template.doBoot(null, init);
|
||||
template.initBeanContainer(weld);
|
||||
|
||||
Reference in New Issue
Block a user