Skip to content

Instantly share code, notes, and snippets.

@christopherthielen
Forked from wonderb0lt/OverviewResource.java
Last active August 29, 2015 14:19
Show Gist options
  • Save christopherthielen/4a4de3d3c3487c331352 to your computer and use it in GitHub Desktop.
Save christopherthielen/4a4de3d3c3487c331352 to your computer and use it in GitHub Desktop.
package com.mediture.truchart.rest.resources;
import java.util.*;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.core.ResourceInvoker;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.ResourceMethodRegistry;
import org.jboss.resteasy.util.WeightedMediaType;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* A resource that displays a list of available
* endpoints (which is helpful to see for debugging purposes).
*
* @author Patrick Stegmann
* http://stackoverflow.com/questions/19795391/list-all-exposed-available-endpoints-of-resteasy-service
* https://gist.github.com/wonderb0lt/10731371
*/
@Path("/api/v1/routes")
public class OverviewResource {
private static final class MethodDescription {
private String method;
private String fullPath;
private MediaType[] produces;
private MediaType[] consumes;
public MethodDescription(String method, String fullPath, MediaType[] produces, MediaType[] consumes) {
super();
this.method = method;
this.fullPath = fullPath;
this.produces = produces;
this.consumes = consumes;
}
}
private static final class ResourceDescription {
private String basePath;
private List<MethodDescription> calls;
public ResourceDescription(String basePath) {
this.basePath = basePath;
this.calls = Lists.newArrayList();
}
public void addMethod(String path, ResourceMethodInvoker method) {
for (String verb : method.getHttpMethods()) {
calls.add(new MethodDescription(verb, path, method.getProduces(), method.getConsumes()));
}
}
private static String mostPreferredOrNull(List<WeightedMediaType> preferred) {
if (preferred.isEmpty()) {
return null;
}
else {
return preferred.get(0).toString();
}
}
public static List<ResourceDescription> fromBoundResourceInvokers(Set<Map.Entry<String, List<ResourceInvoker>>> bound) {
Map<String, ResourceDescription> descriptions = Maps.newHashMap();
for (Map.Entry<String, List<ResourceInvoker>> entry : bound) {
ResourceMethodInvoker aMethod = (ResourceMethodInvoker) entry.getValue().get(0);
String basePath = aMethod.getMethod().getDeclaringClass().getAnnotation(Path.class).value();
String thisPath = Optional.ofNullable(aMethod.getMethod()
.getAnnotation(Path.class))
.map(a -> basePath + a.value())
.orElse(basePath);
if (!descriptions.containsKey(thisPath)) {
descriptions.put(thisPath, new ResourceDescription(thisPath));
}
for (ResourceInvoker invoker : entry.getValue()) {
ResourceMethodInvoker methodinvoker = (ResourceMethodInvoker) invoker;
descriptions.get(thisPath).addMethod(basePath, methodinvoker);
}
}
return Lists.newLinkedList(descriptions.values());
}
}
@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public List<ResourceDescription> getAvailableEndpoints(@Context Dispatcher dispatcher) {
return ResourceDescription.fromBoundResourceInvokers(getResoureDescriptions((ResourceMethodRegistry) dispatcher.getRegistry()));
}
@GET
@Path("/")
@Produces(MediaType.TEXT_HTML)
public Response getAvailableEndpointsHtml(@Context Dispatcher dispatcher) {
// Yeah, yeah, HTML per StringBuilder. I can't be bovvered to make a JSP :D
StringBuilder sb = new StringBuilder();
List<ResourceDescription> descriptions = getAvailableEndpoints(dispatcher);
sb.append("<h1>").append("REST interface overview").append("</h1>");
for (ResourceDescription resource : descriptions) {
sb.append("<h2>").append(resource.basePath).append("</h2>");
sb.append("<ul>");
for (MethodDescription method : resource.calls) {
sb.append("<li> ").append(method.method).append(" ");
sb.append("<strong>").append(method.fullPath).append("</strong>");
sb.append("<ul>");
if (method.consumes != null) {
sb.append("<li>").append("Consumes: ").append(Arrays.asList(method.consumes)).append("</li>");
}
if (method.produces != null) {
sb.append("<li>").append("Produces: ").append(Arrays.asList(method.produces)).append("</li>");
}
sb.append("</ul>");
}
sb.append("</ul>");
}
return Response.ok(sb.toString()).build();
}
private Set<Map.Entry<String, List<ResourceInvoker>>> getResoureDescriptions(ResourceMethodRegistry registry) {
return registry.getBounded().entrySet();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment