-
-
Save christopherthielen/4a4de3d3c3487c331352 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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