ResourceContext.matchResource(URI)的Jersey 2替换是什么?

如何在Jersey 2.0下找出任意URI映射到的资源? 在Jersey 1.x下,我使用ResourceContext.matchResource(URI) 。

我正在尝试做什么 :我正在尝试处理通过URI引用另一个资源的传入请求。 例如,这是将用户链接到部门的操作。

POST /departments/5 { "user": "http://example.com/users/10" } 

POST / departments / 5解析为:

 class DepartmentResource { @POST void linkUser() { ... } } 

为了兑现这个请求,我需要将URI解析回UserResource并从那里解析为其数据库ID。 将@Context UriInfo添加到linkUser()将无济于事,因为此UriInfo对应于部门的URI而不是用户的URI。

更新 :我在https://github.com/eclipse-ee4j/jersey/issues/2444上提交了一份错误报告

UPDATE2 :发布了一个后续问题: Jersey2:从资源导航到实例

如果您在容器中运行,则可以执行以下操作:

 // Classes that may be of interest import org.glassfish.jersey.server.ExtendedResourceContext; import org.glassfish.jersey.server.model.Invocable; import org.glassfish.jersey.server.model.Parameter; import org.glassfish.jersey.server.model.Resource; import org.glassfish.jersey.server.model.ResourceMethod; @Context private ExtendedResourceContext rconfig; public Resource dumpResources(String relUrl){ List parents = rconfig.getResourceModel().getRootResources(); for(Resource res: parents){ if(relUrl.startsWith(res.getPath()){ return res; } } } 

如果您有一个包含子资源的更复杂的层次结构,您可以使用以下方法以递归方式下降到其子项中,或者您可以创建ResourceModelVisitor并通过以下方式使用访问者模式:

 rconfig.getResourceModel().accept( (ResourceModelVisitor) visitor); 

可能还有另一种解决方案,但我可以validation这是否有效。 我使用这种模式动态记录我的资源(而不是wadl)

回答我自己的问题:

 import com.google.common.primitives.Ints; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.ws.rs.PathParam; import javax.ws.rs.ProcessingException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import org.bitbucket.cowwoc.preconditions.Preconditions; import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.jersey.server.ExtendedResourceContext; import org.glassfish.jersey.server.model.Resource; import org.glassfish.jersey.server.model.ResourceMethod; import org.glassfish.jersey.uri.PathPattern; import org.glassfish.jersey.uri.UriComponent; import org.glassfish.jersey.uri.UriTemplate; [...] /** * Returns the resource associated with a URI. * 

* WARNING: The input URI must be encoded according to the rule of {@link Path#value}. *

* @param the resource type * @param uri the encoded URI * @param type the resource type to return * @return the resource associated with the URI * @throws NullPointerException if uri or type are null * @throws IllegalArgumentException if the URI does not map to this resource type * @throws ProcessingException if an exception occurs while creating the resource */ public T getByUri(URI uri, Class type) throws NullPointerException, IllegalArgumentException, ProcessingException { Preconditions.requireThat(uri, "uri").isNotNull(); Preconditions.requireThat(type, "type").isNotNull(); UriInfo uriInfo = serviceLocator.getService(UriInfo.class); ExtendedResourceContext resourceContext = serviceLocator. getService(ExtendedResourceContext.class); if (uriInfo.getBaseUri().relativize(uri).normalize().getPath().startsWith("..")) { throw new IllegalArgumentException("The uri did not originate from this server: " + uri + ". The expected base URI is: " + uriInfo.getBaseUri()); } String path = uri.getRawPath(); Resource match = findMatchingResource(resourceContext.getResourceModel().getRootResources(), path); if (match == null) throw new IllegalArgumentException(uri + " did not match any registered resource"); Class matchedType = getType(match); if (!type.isAssignableFrom(matchedType)) { throw new IllegalArgumentException(uri + " is not of type " + type + ". It is of type: " + matchedType); } @SuppressWarnings("unchecked") T result = (T) createResource(match, path); return result; } /** * Returns the resource or sub-resource that matches a path segment. *

* @param resources a list of candidate resources * @param path a path segment * @return null if no match is found * @throws NullPointerException if resources or path are null */ private Resource findMatchingResource(List resources, String path) throws NullPointerException { for (Resource resource: resources) { PathPattern pattern = resource.getPathPattern(); MatchResult matcher = pattern.match(path); if (matcher == null) continue; assert (matcher.groupCount() >= 1): "resource: " + resource + ", path: " + resource.getPath() + ", match: " + matcher; // The segment not consumed by the parent resource String remaining = matcher.group(matcher.groupCount()); if (remaining != null) remaining = remaining.trim(); if (remaining == null || remaining.isEmpty() || remaining.equals("/")) return resource; Resource descendant = findMatchingResource(resource.getChildResources(), remaining); if (descendant != null) return descendant; } return null; } /** * @param resource a resource * @return the user class the resource references * @throws NullPointerException resource is null */ private Class getType(Resource resource) throws NullPointerException { try { ResourceMethod resourceLocator = resource.getResourceLocator(); if (resourceLocator != null) return (Class) resourceLocator.getInvocable().getResponseType(); for (String name: resource.getNames()) { // ASSUMPTION: Merged resource models contain only one JAX-RS resource class. // Other resource models (eg corresponding to subresource methods) are named: "[unnamed]". if (name.isEmpty() || !Character.isAlphabetic(name.charAt(0))) continue; return Class.forName(name, true, getClass().getClassLoader()); } throw new AssertionError("Resources must have at least one name: " + resource); } catch (ClassNotFoundException e) { // ASSUMPTION: The metadata cannot exist unless the class was already loaded by the ClassLoader. throw new AssertionError(e); } } /** * Creates the resource associated with a URI path. *

* @param resource the resource type * @param path the URI path * @return the resource * @throws NullPointerException if resource or path are null * @throws IllegalArgumentException if the URI does not map to this resource type * @throws ProcessingException if an exception occurs while creating the resource */ private Object createResource(Resource resource, String path) throws NullPointerException, IllegalArgumentException, ProcessingException { List resourceTree = getAncestorTree(resource); // Process the root of the tree Resource rootResource = resourceTree.get(0); Object parentResource = serviceLocator.getService(getType(rootResource)); if (resourceTree.size() == 1) return parentResource; // The path segment not matched by the root String remaining = getRemainingPath(rootResource, path); // Process the rest of the tree for (Resource currentResource: resourceTree.subList(1, resourceTree.size())) { List stringArguments = getStringArguments(currentResource, remaining); remaining = getRemainingPath(currentResource, remaining); Method resourceLocator = currentResource.getResourceLocator().getInvocable(). getHandlingMethod(); // Convert the resource locator arguments from String to the expected type Class[] parameterTypes = resourceLocator.getParameterTypes(); Object[] arguments = new Object[parameterTypes.length]; for (int i = 0, size = stringArguments.size(); i < size; ++i) { Class expectedType = parameterTypes[i]; arguments[i] = parse(stringArguments.get(i), expectedType); } // Invoke the resource locator try { parentResource = resourceLocator.invoke(parentResource, arguments); } catch (ReflectiveOperationException e) { throw new ProcessingException(e); } } return parentResource; } /** * @param resource a resource * @param path a path segment matched by the resource * @return the path segment not consumed by the resource */ private String getRemainingPath(Resource resource, String path) { MatchResult matcher = resource.getPathPattern().match(path); assert (matcher.groupCount() >= 1): "resource: " + resource + ", path: " + resource.getPath() + ", match: " + matcher; // The remaining path (not consumed by the parent resource) return matcher.group(matcher.groupCount()); } /** * @param method a resource locator * @param path the path segment that the method matched * @return a list of the string arguments passed into the method */ private List getStringArguments(Resource resource, String path) { // Parse the URI into template key-value pairs MatchResult match = resource.getPathPattern().match(path); List templateKeys = resource.getPathPattern().getTemplate().getTemplateVariables(); Map template = new HashMap<>((int) Math.ceil(templateKeys.size() / 0.75)); List groupNumbers = getGroupNumbers(resource.getPathPattern()); for (int i = 0, size = templateKeys.size(); i < size; ++i) template.put(templateKeys.get(i), match.group(groupNumbers.get(i))); Method resourceLocator = resource.getResourceLocator().getInvocable().getHandlingMethod(); List result = new ArrayList<>(resourceLocator.getParameters().length); for (Parameter parameter: resourceLocator.getParameters()) { PathParam annotation = parameter.getAnnotation(PathParam.class); if (annotation == null) continue; String key = annotation.value(); String value = template.get(key); result.add(UriComponent.decode(value, UriComponent.Type.PATH)); } return result; } /** * @param pattern a PathPattern * @return the group numbers of the top-level capturing groups */ private List getGroupNumbers(PathPattern pattern) { int[] candidate = pattern.getGroupIndexes(); if (candidate.length > 0) return Ints.asList((int[]) candidate); UriTemplate template = pattern.getTemplate(); List result = new ArrayList<>(template.getTemplateVariables().size()); String regex = template.getPattern().getRegex(); // Find an unescaped open or close brace Matcher braces = Pattern.compile("[^\\\\](\\(|\\))").matcher(regex); int level = 0; int groupNumber = 1; while (braces.find()) { switch (braces.group(1)) { case "(": { if (level == 0) result.add(groupNumber); ++groupNumber; ++level; break; } case ")": { --level; assert (level >= 0): regex; break; } default: throw new AssertionError("Unexpected character: " + braces.group(1)); } } return result; } /** * @param resource a resource * @return a list of resources, from the top-most ancestor down to the resource itself * @throws NullPointerException resource is null */ private List getAncestorTree(Resource resource) throws NullPointerException { List result = new ArrayList<>(10); Resource current = resource; do { result.add(current); current = current.getParent(); } while (current != null); Collections.reverse(result); return result; } /** * Converts a String to a different type. *

* @param value the string value * @param type the desired type * @return the parsed value */ private Object parse(String value, Class type) { // PERFORMANCE: Because the small number of entries, it's faster to do a linear search than // using a HashMap. Testing under Java 8. if (type.isAssignableFrom(String.class)) return value; if (type.isAssignableFrom(boolean.class) || type.isAssignableFrom(Boolean.class)) return Boolean.valueOf(value); if (type.isAssignableFrom(char.class) || type.isAssignableFrom(Character.class)) { if (value.length() != 0) throw new ProcessingException("Expecting a single character. Got: \"" + value + "\""); return value.charAt(0); } if (type.isAssignableFrom(byte.class) || type.isAssignableFrom(Byte.class)) return Byte.valueOf(value); if (type.isAssignableFrom(short.class) || type.isAssignableFrom(Short.class)) return Short.valueOf(value); if (type.isAssignableFrom(int.class) || type.isAssignableFrom(Integer.class)) return Integer.valueOf(value); if (type.isAssignableFrom(long.class) || type.isAssignableFrom(Long.class)) return Long.valueOf(value); if (type.isAssignableFrom(float.class) || type.isAssignableFrom(Float.class)) return Float.valueOf(value); if (type.isAssignableFrom(double.class) || type.isAssignableFrom(Double.class)) return Double.valueOf(value); throw new AssertionError("Unexpected type: " + type); }

你想在哪里使用它? 如果我是对的,您可以使用上下文变量,UriInfo实例来获取匹配的资源:

 @Path("books") public class BookResource { private static final Logger LOGGER = Logger.getLogger(BookResource.class); @Context UriInfo uriInfo; @Path("{bookId:[0-9]*}") @GET @Produces({ MediaType.APPLICATION_XML }) public Book getBookByPath(@PathParam("bookId") final Long bookId,@Context final Application application) { final Book book = new Book(bookId); BookResource.LOGGER.debug(book); List matchedResources= uriInfo.getMatchedResources(); for (Object matchedResource : matchedResources) { LOGGER.debug(matchedResource.getClass()); } LOGGER.debug(uriInfo.getAbsolutePath()); return book; } }