Skip to content

Commit

Permalink
Add a list of REST Client Reactive clients to Dev Ui
Browse files Browse the repository at this point in the history
  • Loading branch information
michalszynkiewicz committed Nov 19, 2021
1 parent 371b883 commit 8e7a179
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import io.quarkus.restclient.config.RestClientConfigUtils;
import io.quarkus.restclient.config.RestClientsConfig;
import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem;
import io.quarkus.runtime.LaunchMode;

class RestClientReactiveProcessor {

Expand Down Expand Up @@ -324,13 +325,16 @@ AdditionalBeanBuildItem registerProviderBeans(CombinedIndexBuildItem combinedInd
}

@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void addRestClientBeans(Capabilities capabilities,
CombinedIndexBuildItem combinedIndexBuildItem,
BuildProducer<GeneratedBeanBuildItem> generatedBeans,
RestClientReactiveConfig clientConfig) {
RestClientReactiveConfig clientConfig,
RestClientRecorder recorder) {

CompositeIndex index = CompositeIndex.create(combinedIndexBuildItem.getIndex());
Set<AnnotationInstance> registerRestClientAnnos = new HashSet<>(index.getAnnotations(REGISTER_REST_CLIENT));
Map<String, String> configKeys = new HashMap<>();
for (AnnotationInstance registerRestClient : registerRestClientAnnos) {
ClassInfo jaxrsInterface = registerRestClient.target().asClass();
// for each interface annotated with @RegisterRestClient, generate a $$CDIWrapper CDI bean that can be injected
Expand All @@ -353,6 +357,10 @@ void addRestClientBeans(Capabilities capabilities,

// CLASS LEVEL
final Optional<String> configKey = getConfigKey(registerRestClient);

configKey.ifPresent(
key -> configKeys.put(jaxrsInterface.name().toString(), key));

final ScopeInfo scope = computeDefaultScope(capabilities, ConfigProvider.getConfig(), jaxrsInterface,
configKey, clientConfig);
// add a scope annotation, e.g. @Singleton
Expand Down Expand Up @@ -422,6 +430,9 @@ void addRestClientBeans(Capabilities capabilities,
}
}
}
if (LaunchMode.current() == LaunchMode.DEVELOPMENT) {
recorder.setConfigKeys(configKeys);
}
}

private boolean isRestMethod(MethodInfo method) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.quarkus.rest.client.reactive.deployment.devconsole;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.runtime.BeanLookupSupplier;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem;
import io.quarkus.rest.client.reactive.runtime.devconsole.RestClientsContainer;

public class RestClientReactiveDevConsoleProcessor {

@BuildStep(onlyIf = IsDevelopment.class)
public DevConsoleRuntimeTemplateInfoBuildItem devConsoleInfo() {
return new DevConsoleRuntimeTemplateInfoBuildItem("devRestClients",
new BeanLookupSupplier(RestClientsContainer.class));
}

@BuildStep(onlyIf = IsDevelopment.class)
public AdditionalBeanBuildItem beans() {
return AdditionalBeanBuildItem.unremovableOf(RestClientsContainer.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<a href="{urlbase}/rest-clients" class="badge badge-light">
<i class="fa fa-external-link-alt fa-fw"></i>
REST clients
</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{#include main fluid=true}
{#style}
.annotation {
color: gray;
font-style: italic;
}
span.larger-badge {
font-size: 0.9em;
}
span.app-class {
cursor:pointer;
color:blue;
text-decoration:underline;
}

{/style}

{#script}
$(document).ready(function(){
if (!ideKnown()) {
return;
}
$(".class-candidate").each(function() {
var className = $(this).text();
if (appClassLang(className)) {
$(this).addClass("app-class");
}
});

$(".app-class").on("click", function() {
openInIDE($(this).text());
});
});
{/script}

{#title}REST Client classes{/title}
{#body}
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th scope="col">#</th>
<th scope="col">Client interface</th>
<th scope="col">Is a CDI bean (injectable with @RestClient)</th>
<th scope="col">Config prefix</th>
</tr>
</thead>
<tbody>
{#for client in info:devRestClients.clientData.clients}
<tr>
<td>{client_count}.</td>
<td>
<span class="class-candidate">{client.interfaceClass}</span>
</td>
<td>
<i>{client.isBean}</i>
</td>
<td>
{client.configKey}
</td>
{/for}
</tbody>
</table>
{/body}
{/include}
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
package io.quarkus.rest.client.reactive.runtime;

import java.util.Map;

import org.eclipse.microprofile.rest.client.spi.RestClientBuilderResolver;

import io.quarkus.runtime.annotations.Recorder;

@Recorder
public class RestClientRecorder {
private static volatile Map<String, String> configKeys;

public void setConfigKeys(Map<String, String> configKeys) {
RestClientRecorder.configKeys = configKeys;
}

public static Map<String, String> getConfigKeys() {
return configKeys;
}

public void setRestClientBuilderResolver() {
RestClientBuilderResolver.setInstance(new BuilderResolver());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package io.quarkus.rest.client.reactive.runtime.devconsole;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Singleton;

import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.reactive.client.impl.ClientProxies;
import org.jboss.resteasy.reactive.client.spi.ClientContext;
import org.jboss.resteasy.reactive.client.spi.ClientContextResolver;

import io.quarkus.arc.Unremovable;
import io.quarkus.arc.profile.IfBuildProfile;
import io.quarkus.rest.client.reactive.runtime.RestClientRecorder;

@IfBuildProfile("dev")
@Unremovable
@Singleton
public class RestClientsContainer {

public static final ClientContextResolver CLIENT_CONTEXT_RESOLVER = ClientContextResolver.getInstance();

@Inject
@RestClient
Instance<Object> injectableClients;

/**
* Used in Dev UI
*
* @return info about exposed clients
*/
public RestClientData getClientData() {
ClientContext context = CLIENT_CONTEXT_RESOLVER.resolve(Thread.currentThread().getContextClassLoader());
ClientProxies.ClientData clientData = context.getClientProxies().getClientData();

List<RestClientInfo> restClients = new ArrayList<>();
List<PossibleRestClientInfo> possibleRestClients = new ArrayList<>();

for (Class<?> clientClass : clientData.clientClasses) {
Instance<?> select = injectableClients.select(clientClass);
String interfaceName = clientClass.getName();
if (select.isResolvable()) {
String configKey = RestClientRecorder.getConfigKeys().get(interfaceName);
if (configKey == null) {
configKey = String.format("\"%s\"", interfaceName);
}
restClients.add(new RestClientInfo(interfaceName, true, configKey));
} else {
restClients.add(new RestClientInfo(interfaceName, false, null));
}
}
for (Map.Entry<Class<?>, String> clientEntry : clientData.failures.entrySet()) {
possibleRestClients.add(new PossibleRestClientInfo(clientEntry.getKey().getName(), clientEntry.getValue()));
}
return new RestClientData(restClients, possibleRestClients);
}

public static class RestClientData {
public final List<RestClientInfo> clients;
public final List<PossibleRestClientInfo> possibleClients;

public RestClientData(List<RestClientInfo> clients, List<PossibleRestClientInfo> possibleClients) {
this.clients = clients;
this.possibleClients = possibleClients;
}
}

public static class RestClientInfo {
public final String interfaceClass;
public final boolean isBean;
public final String configKey;

public RestClientInfo(String interfaceClass, boolean isBean, String configKey) {
this.interfaceClass = interfaceClass;
this.isBean = isBean;
this.configKey = configKey == null ? "" : String.format("quarkus.rest.client.%s", configKey);
}
}

public static class PossibleRestClientInfo {
public final String jaxrsClassName;
public final String failure;

public PossibleRestClientInfo(String jaxrsClassName, String failure) {
this.jaxrsClassName = jaxrsClassName;
this.failure = failure;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jboss.resteasy.reactive.client.impl;

import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import javax.ws.rs.client.WebTarget;
Expand Down Expand Up @@ -31,4 +32,19 @@ public <T> T get(Class<?> clazz, WebTarget webTarget) {
//noinspection unchecked
return (T) function.apply(webTarget);
}

// for dev console
public ClientData getClientData() {
return new ClientData(clientProxies.keySet(), failures);
}

public static class ClientData {
public final Collection<Class<?>> clientClasses;
public final Map<Class<?>, String> failures;

public ClientData(Collection<Class<?>> clientClasses, Map<Class<?>, String> failures) {
this.clientClasses = clientClasses;
this.failures = failures;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ public interface ClientContextResolver {
ClientContext resolve(ClassLoader classLoader);

static ClientContextResolver getInstance() {
ServiceLoader<ClientContextResolver> services = ServiceLoader.load(ClientContextResolver.class,
Thread.currentThread().getContextClassLoader());
return getInstance(Thread.currentThread().getContextClassLoader());
}

static ClientContextResolver getInstance(ClassLoader classLoader) {
ServiceLoader<ClientContextResolver> services = ServiceLoader.load(ClientContextResolver.class, classLoader);
ClientContextResolver selected = null;
for (ClientContextResolver i : services) {
if (selected != null) {
Expand Down

0 comments on commit 8e7a179

Please sign in to comment.