Список всех открытых/доступных конечных точек службы RestEasy?

Можно ли перечислить все открытые/доступные конечные точки службы RestEasy простым способом?

Ответы

Ответ 1

EDIT:

См. этот пример для "более чистого" примера: https://gist.github.com/wonderb0lt/10731371


Да, это возможно. Возможно, вы хотели бы знать, как это сделать?:)

Вот пример "быстрый n-грязный":

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;
        }

    }
}

Ответ 2

Мне пришлось настроить "чистый" пример, который был превосходным для начала. Я использую RestEasy 3.07 и хочу также иметь каждый метод аннотации Path. Я надеюсь, что эта модификация может помочь другим.

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

}

}

(В другой заметке, возможно, есть что-то доступное, или я могу приступить к работе, чтобы смоделировать список ресурсов и описание, которое ServiceStack делает так красиво: http://mono.servicestack.net/Content/Images/MetadataIndex.png)

Ответ 3

Существует плагин RestEasy, "статистика", который предоставляет .../resteasy/registry.

Он должен быть зарегистрирован в web.xml:

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

Пример ответа:

<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>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxb-provider</artifactId>
    <version>3.0.8.Final</version>
</dependency>

См. например. EAP docs и этот EAP 7 Jira

Ответ 4

Если кто-то все еще смотрит нажмите "/resteasy/registry" в своем приложении и

предоставляет XML-вывод всех зарегистрированных конечных точек, связанных классов/методов и т.д.

FYI resteasy-jaxb-provider предоставляет эту функциональность