RestEasyサービスの公開されている/利用可能なすべてのエンドポイントを簡単な方法で一覧表示することは可能ですか?
.../resteasy/registry
を公開するRestEasyプラグイン「stats」があります。
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ドキュメント および このEAP 7 Jira
そもそも優れた「クリーナー」の例を調整する必要がありました。 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 )
編集:
「よりクリーンな」例については、この要点を参照してください: https://Gist.github.com/wonderb0lt/10731371
はい、可能です。おそらくあなたはその方法を知りたいですか? :)
これが「quick-n-dirty」の例です。
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;
}
}
}
古い投稿ですが、ここで答えます。
これは、JBossに同梱されているRestEasyの実装です。あなたはそれを使うことができます、あるいはあなたはあなた自身を書くことができます。実装は、各RestEasyリソースのuriTemplate文字列を見つけることができる配列プロパティを持つオブジェクトを返します。
すべてのエントリを繰り返し処理して、必要な情報を取得する必要があります。
RegistryData.entries.get(index).uriTemplate
Org.jboss.resteasy.plugins.stats.RegistryStatsResource.getメソッドの実装:
public RegistryData get() throws JAXBException {
ResourceMethodRegistry registry = (ResourceMethodRegistry)ResteasyProviderFactory.getContextData(Registry.class);
RegistryData data = new RegistryData();
Iterator i$ = registry.getRoot().getBounded().keySet().iterator();
label85:
while(i$.hasNext()) {
String key = (String)i$.next();
List<ResourceInvoker> invokers = (List)registry.getRoot().getBounded().get(key);
RegistryEntry entry = new RegistryEntry();
data.getEntries().add(entry);
entry.setUriTemplate(key);
Iterator i$ = invokers.iterator();
while(true) {
while(true) {
if (!i$.hasNext()) {
continue label85;
}
ResourceInvoker invoker = (ResourceInvoker)i$.next();
if (invoker instanceof ResourceMethod) {
ResourceMethod rm = (ResourceMethod)invoker;
Object method;
for(Iterator i$ = rm.getHttpMethods().iterator(); i$.hasNext(); entry.getMethods().add(method)) {
String httpMethod = (String)i$.next();
method = null;
if (httpMethod.equals("GET")) {
method = new GetResourceMethod();
} else if (httpMethod.equals("PUT")) {
method = new PutResourceMethod();
} else if (httpMethod.equals("DELETE")) {
method = new DeleteResourceMethod();
} else if (httpMethod.equals("POST")) {
method = new PostResourceMethod();
} else if (httpMethod.equals("OPTIONS")) {
method = new OptionsResourceMethod();
} else if (httpMethod.equals("TRACE")) {
method = new TraceResourceMethod();
} else if (httpMethod.equals("HEAD")) {
method = new HeadResourceMethod();
}
((ResourceMethodEntry)method).setClazz(rm.getResourceClass().getName());
((ResourceMethodEntry)method).setMethod(rm.getMethod().getName());
AtomicLong stat = (AtomicLong)rm.getStats().get(httpMethod);
if (stat != null) {
((ResourceMethodEntry)method).setInvocations(stat.longValue());
} else {
((ResourceMethodEntry)method).setInvocations(0L);
}
MediaType[] arr$;
int len$;
int i$;
MediaType mediaType;
if (rm.getProduces() != null) {
arr$ = rm.getProduces();
len$ = arr$.length;
for(i$ = 0; i$ < len$; ++i$) {
mediaType = arr$[i$];
((ResourceMethodEntry)method).getProduces().add(mediaType.toString());
}
}
if (rm.getConsumes() != null) {
arr$ = rm.getConsumes();
len$ = arr$.length;
for(i$ = 0; i$ < len$; ++i$) {
mediaType = arr$[i$];
((ResourceMethodEntry)method).getConsumes().add(mediaType.toString());
}
}
}
} else {
ResourceLocator rl = (ResourceLocator)invoker;
SubresourceLocator locator = new SubresourceLocator();
locator.setClazz(rl.getMethod().getDeclaringClass().getName());
locator.setMethod(rl.getMethod().getName());
entry.setLocator(locator);
}
}
}
}
return data;
}