Is it possible to list all exposed/available endpoints of RestEasy service in a simple way?
I had to adjust the "cleaner" example which was excellent to begin with. I'm using RestEasy 3.07 and wanted to also have each method's Path annotation value. I hope this modification can be of help to others.
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
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.ResourceMethodInvoker;
import org.jboss.resteasy.core.ResourceMethodRegistry;
import org.springframework.stereotype.Component;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@Component
@Path("/overview")
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 calls;
public ResourceDescription(String basePath)
{
this.basePath = basePath;
this.calls = Lists.newArrayList();
}
public void addMethod(String path, ResourceMethodInvoker method)
{
String produces = mostPreferredOrNull(method.getProduces());
String consumes = mostPreferredOrNull(method.getConsumes());
for (String verb : method.getHttpMethods())
{
calls.add(new MethodDescription(verb, path, produces, consumes));
}
}
private static String mostPreferredOrNull(MediaType[] mediaTypes)
{
if (mediaTypes == null || mediaTypes.length < 1)
{
return null;
}
else
{
return mediaTypes[0].toString();
}
}
public static List fromBoundResourceInvokers(
Set>> bound)
{
Map descriptions = Maps.newHashMap();
for (Map.Entry> entry : bound)
{
Method aMethod = ((ResourceMethodInvoker) entry.getValue().get(0)).getMethod();
String basePath = aMethod.getDeclaringClass().getAnnotation(Path.class).value();
if (!descriptions.containsKey(basePath))
{
descriptions.put(basePath, new ResourceDescription(basePath));
}
for (ResourceInvoker invoker : entry.getValue())
{
ResourceMethodInvoker method = (ResourceMethodInvoker) invoker;
String subPath = null;
for(Annotation annotation : method.getMethodAnnotations())
{
if(annotation.annotationType().equals(Path.class))
{
subPath = ((Path) annotation).value();
break;
}
}
descriptions.get(basePath).addMethod(basePath + subPath, method);
}
}
return Lists.newLinkedList(descriptions.values());
}
}
@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public List getAvailableEndpoints(@Context Dispatcher dispatcher)
{
ResourceMethodRegistry registry = (ResourceMethodRegistry) dispatcher.getRegistry();
return ResourceDescription.fromBoundResourceInvokers(registry.getBounded().entrySet());
}
@GET
@Path("/")
@Produces(MediaType.TEXT_HTML)
public Response getAvailableEndpointsHtml(@Context Dispatcher dispatcher)
{
StringBuilder sb = new StringBuilder();
ResourceMethodRegistry registry = (ResourceMethodRegistry) dispatcher.getRegistry();
List descriptions = ResourceDescription.fromBoundResourceInvokers(registry.getBounded()
.entrySet());
sb.append("").append("REST interface overview").append("
");
for (ResourceDescription resource : descriptions)
{
sb.append("").append(resource.basePath).append("
");
sb.append("");
for (MethodDescription method : resource.calls)
{
sb.append("- ").append(method.method).append(" ");
sb.append("").append(method.fullPath).append("");
sb.append("
");
if (method.consumes != null)
{
sb.append("- ").append("Consumes: ").append(method.consumes).append("
");
}
if (method.produces != null)
{
sb.append("- ").append("Produces: ").append(method.produces).append("
");
}
sb.append("
");
}
sb.append("
");
}
return Response.ok(sb.toString()).build();
}
}
(On another note, perhaps there is something available, or I can begin work on, to model the resource listing and description that ServiceStack does so nicely: http://mono.servicestack.net/Content/Images/MetadataIndex.png)