Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List all exposed/available endpoints of RestEasy service?

Tags:

jboss

resteasy

Is it possible to list all exposed/available endpoints of RestEasy service in a simple way?

like image 305
fugbixed Avatar asked Nov 05 '13 17:11

fugbixed


People also ask

What is RESTEasy Web services?

RESTEasy is a JBoss / Red Hat project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications. It is an implementation of the Jakarta RESTful Web Services, an Eclipse Foundation specification that provides a Java API for RESTful Web Services over the HTTP protocol.

Does RESTEasy use Jackson?

Resteasy supports both Jackson 1.9. x and Jackson 2.2.

What is RESTEasy Jackson provider?

Jackson, RESTEasy. RESTEasy is JBOSS provided implementation of JAX-RS specification for building RESTful Web Services and RESTful Java applications. Though this is not limited to be used in JBOSS only, and you can use with other servers also.


3 Answers

There is a RestEasy plugin, "stats", which exposes .../resteasy/registry.

It needs to be registered in web.xml:

<context-param>
    <param-name>resteasy.resources</param-name>
    <param-value>org.jboss.resteasy.plugins.stats.RegistryStatsResource</param-value>
</context-param>

Example response:

<registry>
    <resource uriTemplate="/resource">
        <delete class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="delete"
                invocations="0"/>
        <head class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="head" invocations="0"/>
    </resource>
    <resource uriTemplate="/locator">
        <locator class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="getLocator"/>
    </resource>
    <resource uriTemplate="/resteasy/registry">
        <get class="org.jboss.resteasy.plugins.stats.RegistryStatsResource" method="get" invocations="2">
            <produces>application/xml</produces>
            <produces>application/json</produces>
        </get>
    </resource>
    <resource uriTemplate="/entry/{foo:.*}">
        <post class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="post" invocations="0">
            <produces>text/xml</produces>
            <consumes>application/json</consumes>
        </post>
        <put class="org.jboss.resteasy.test.providers.jaxb.resource.StatsResource" method="put" invocations="0">
            <produces>text/xml</produces>
            <consumes>application/json</consumes>
        </put>
    </resource>
</registry>

Maven dependency:

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxb-provider</artifactId>
    <version>3.0.8.Final</version>
</dependency>

See eg. EAP docs and this EAP 7 Jira

like image 160
Ondra Žižka Avatar answered Oct 24 '22 10:10

Ondra Žižka


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<MethodDescription> 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<ResourceDescription> fromBoundResourceInvokers(
            Set<Map.Entry<String, List<ResourceInvoker>>> bound)
    {
        Map<String, ResourceDescription> descriptions = Maps.newHashMap();

        for (Map.Entry<String, List<ResourceInvoker>> 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<ResourceDescription> 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<ResourceDescription> descriptions = ResourceDescription.fromBoundResourceInvokers(registry.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();

}

}

(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)

like image 29
thefeather Avatar answered Oct 24 '22 12:10

thefeather


EDIT:

See this gist for a "cleaner" example: https://gist.github.com/wonderb0lt/10731371


Yes, it's possible. Perhaps you would like to know how? :)

Here's a "quick-n-dirty" example:

import org.jboss.resteasy.annotations.providers.jaxb.Formatted;
import org.jboss.resteasy.annotations.providers.jaxb.Wrapped;
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.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.Test;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class PrintAllResourcesTest {

    @Test
    public void name_StateUnderTest_ExpectedBehavior() throws Exception {
        Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();

        dispatcher.getRegistry().addSingletonResource(new MetaService());
        dispatcher.getRegistry().addSingletonResource(new Service());

        MockHttpResponse response = new MockHttpResponse();
        MockHttpRequest request = MockHttpRequest.get("/meta")
                .accept(MediaType.APPLICATION_XML);


        dispatcher.invoke(request, response);

         /*<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
         <resources>
            <resource method="GET">/service/</resource>
            <resource method="POST">/service/</resource>
         </resources>*/
        String result = response.getContentAsString();
    }

    @XmlRootElement(name = "resource")
    public static final class JaxRsResource {
        @XmlAttribute String method;
        @XmlValue String uri;

        public JaxRsResource() {}

        public JaxRsResource(String method, String uri) {
            this.method = method;
            this.uri = uri;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            JaxRsResource that = (JaxRsResource) o;

            if (method != null ? !method.equals(that.method) : that.method != null) return false;
            if (uri != null ? !uri.equals(that.uri) : that.uri != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = method != null ? method.hashCode() : 0;
            result = 31 * result + (uri != null ? uri.hashCode() : 0);
            return result;
        }
    }

    @Path("/service")
    public static final class Service {

        @GET
        @Path("/")
        public String getStuff(){
            return "";
        }


        @POST
        @Path("/")
        public String postStuff(){
            return "";
        }
    }


    @Path("/meta")
    public static final class MetaService {
        @Context Dispatcher dispatcher;

        @GET
        @Path("/")
        @Wrapped(element = "resources")
        @Formatted
        public Set<JaxRsResource> getAllResources(){
            Set<JaxRsResource> resources = new HashSet<JaxRsResource>();

            ResourceMethodRegistry registry = (ResourceMethodRegistry) dispatcher.getRegistry();

            for (Map.Entry<String, List<ResourceInvoker>> entry : registry.getRoot().getBounded().entrySet()) {
                for (ResourceInvoker invoker : entry.getValue()) {
                    ResourceMethod method = (ResourceMethod) invoker;

                    if(method.getMethod().getDeclaringClass() == getClass()){
                        continue;
                    }

                    for (String verb : method.getHttpMethods()) {
                        String uri = entry.getKey();
                        resources.add(new JaxRsResource(verb, uri));
                    }
                }
            }

            return resources;
        }

    }
}
like image 28
eiden Avatar answered Oct 24 '22 10:10

eiden



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!