Created
April 15, 2014 13:11
-
-
Save wonderb0lt/10731371 to your computer and use it in GitHub Desktop.
A JAX-RS resource that shows all bound methods in the current context (RestEasy only)
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
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
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.ResourceMethod; | |
import org.jboss.resteasy.core.ResourceMethodRegistry; | |
import org.jboss.resteasy.util.WeightedMediaType; | |
import org.springframework.stereotype.Service; | |
import com.google.common.collect.Lists; | |
import com.google.common.collect.Maps; | |
import com.sonydadc.wldcs.bo.rest.security.Secure; | |
/** | |
* A resource that displays a list of available | |
* endpoints (which is helpful to see for debugging purposes). | |
* | |
* @author Patrick Stegmann | |
*/ | |
@Service | |
@Path("/") | |
public class OverviewResource { | |
private static final class MethodDescription { | |
private String method; | |
private String fullPath; | |
private String produces; | |
private String consumes; | |
public MethodDescription(String method, String fullPath, String produces, String 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, ResourceMethod method) { | |
String produces = mostPreferredOrNull(method.getPreferredProduces()); | |
String consumes = mostPreferredOrNull(method.getPreferredConsumes()); | |
for (String verb : method.getHttpMethods()) { | |
calls.add(new MethodDescription(verb, path, produces, consumes)); | |
} | |
} | |
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) { | |
ResourceMethod aMethod = (ResourceMethod) entry.getValue().get(0); | |
String basePath = aMethod.getMethod().getDeclaringClass().getAnnotation(Path.class).value(); | |
if (!descriptions.containsKey(basePath)) { | |
descriptions.put(basePath, new ResourceDescription(basePath)); | |
} | |
for (ResourceInvoker invoker : entry.getValue()) { | |
ResourceMethod method = (ResourceMethod) invoker; | |
descriptions.get(basePath).addMethod(basePath, method); | |
} | |
} | |
return Lists.newLinkedList(descriptions.values()); | |
} | |
} | |
@GET | |
@Path("/") | |
@Produces(MediaType.APPLICATION_JSON) | |
public List<ResourceDescription> getAvailableEndpoints(@Context Dispatcher dispatcher) { | |
ResourceMethodRegistry registry = (ResourceMethodRegistry) dispatcher.getRegistry(); | |
return ResourceDescription.fromBoundResourceInvokers(registry.getRoot().getBounded().entrySet()); | |
} | |
@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(); | |
ResourceMethodRegistry registry = (ResourceMethodRegistry) dispatcher.getRegistry(); | |
List<ResourceDescription> descriptions = ResourceDescription.fromBoundResourceInvokers(registry.getRoot().getBounded().entrySet()); | |
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(method.consumes).append("</li>"); | |
} | |
if (method.produces != null) { | |
sb.append("<li>").append("Produces: ").append(method.produces).append("</li>"); | |
} | |
sb.append("</ul>"); | |
} | |
sb.append("</ul>"); | |
} | |
return Response.ok(sb.toString()).build(); | |
} | |
} |
Very helpful. With only minor tweaks, it showed all my deployed services.
Thanks, that's really useful. Works fine with Resteasy 2.3.6.Final.
In new versions of RESTEasy, ResourceMethod
has become ResourceMethodInvoker
.
This is great! Can you please add a open source license which allows to embed this class in a commercial application?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks, this is great. I've forked it to get it to work with the version of RESTEasy that we use.