Are subresource locator and resource method on same path illegal? - java

We are building a RESTful JAXRS web service for use with a Jersey HTTP client. We'd like to make heavy use of subresource locators to prevent cluttering code in single large source files. E.g. (minimal example):
#Path("places")
public class PlacesResource {
// Use the following to e.g. GET /places/123
#GET #Path("{id}")
#Produces("application/json")
public Response getPlace(#PathParam("id") int id) {
//...
return Response.ok(null).build();
}
// Use the following to e.g. GET /places/123/comments/42
#Path("{id}")
public PlaceResource place(#PathParam("id") int id) {
Place p = DAO.getInstance().getPlace(id);
return new PlaceResource(p); // singular (a different resource class)
}
}
This works fine. Removing either of these methods makes calls to resources as specified in the leading comments not work. (HTTP response 405: method not allowed)
However, while using this setup the following warning is printed to the Tomcat log:
[http-nio-8084-exec-6] org.glassfish.jersey.internal.Errors.logErrors The following warnings have been detected: WARNING: The resource (or sub resource) Resource{"{id}", 0 child resources, 5 resource methods, 1 sub-resource locator, 4 method handler classes, 0 method handler instances} with path "{id}" contains (sub) resource method(s) and sub resource locator. The resource cannot have both, methods and locator, defined on same path. The locator will be ignored.
It says the locator will be ignored, but it is very much working. What's wrong? By the way, I'd much prefer to be able to use the subresource locator even for path /places/{id}. It should just use the #GET-annotated method in the subresource class; this returns a 405 error code, as stated, though.

Yup, it's illegal. When you add a sub resource, it's responsible for managing its root path as well. So for the path /places/{id} the service wouldn't know which one to use (the method or the sub resource) since they both claim to manage that path. The sub resource locator is indeed ignored, but only for that one ambiguous path (/places/{id}). The path /places/{id}/other stuff is not ambiguous since it doesn't match the GET method's path, so it isn't ignored.
To remove the ambiguity, try modifying the sub resource to only match paths that specify the place ID and other path components. I don't have access to my IDE to test at the moment, but something like this should work:
// Use the following to e.g. GET /places/123/comments/42
#Path("{id}/{otherStuff: [a-zA-Z0-9_/]+}")
public PlaceResource place(#PathParam("id") int id) {
Place p = DAO.getInstance().getPlace(id);
return new PlaceResource(p); // singular (a different resource class)
}

Related

Do path parameters in jax-rs #Path expression need to be slash-separated?

I'm inspecting some code in a JAX-RS springboot microservice that I'm starting to work on. I saw the following (modified):
#POST
#Path("{foo: ([^/]+?)?}{bar: (/[^/]+?)?}")
public Response doit(
#PathParam("foo") String foo,
#PathParam("bar") String bar,
#RequestBody UpdateRequest updateRequest, #Context HttpHeaders httpHeaders);
That #Path value looks odd. Instead of having explicit "/" markers in the string, it's trying to do it through the regex. I'm guessing this can work, because this is existing code, but is this really advisable? Is there any reason that this would be necessary?
I suppose a similar questionable example would be this:
#Path("foo{bar: (/[^/]+?)?}")
Is there any reason this is better than the simpler:
#Path("foo/{bar}")
The JAX-RS specification, specifically the “URI Templates” section under the Resources chapter, has the answer:
Template parameters can optionally specify the regular expression used to match their values. The default value matches any text and terminates at the end of a path segment but other values can be used to alter this behavior, e.g.:
#Path("widgets/{path:.+}")
public class Widget {
...
}
In the above example the Widget resource class will be matched for any request whose path starts with widgets and contains at least one more path segment; the value of the path parameter will be the request path following widgets. E.g. given the request path widgets/small/a the value of path would be small/a.
So, if you don’t provide a customn regex, the default boundary is the /.
Therefore, that complex regex is unnecessary. #Path("{foo}/{bar}" is fine.
Technically, it’s not exactly the same; the regex forces {bar} to include the leading /. Is it worth the complexity of regexes that need extra visual analysis? Not in my opinion.
If you just used
#Path("foo/{bar}")
then calling /foo would lead to a 404, because the / is static and it would require requesting /foo/. But when it's in the regex of bar, it makes it optional. So the example
#Path("foo{bar: (/[^/]+?)?}")
allows you to access the parent resource and the sub resource from the same resource method. As a more realistic example say you have
#Path("customers{id: (/[^/]+?)?}")
With this, we would have a resource method that could handle both accessing a collection resource and an single resource. As opposed to having two separate resource methods, one for each case. For example
#GET
#Path("customers{id: (/[^/]+?)?}")
public Response get(#PathParam("id") String id) {
if (id == null) {
return collection customers collection
} else {
fetch custom by id and return customer.
}
}
That's the only real benefit I can see in this situation. Would probably need more context, maybe some documenting comments from the author as to what they were trying to accomplish. Overall though, IMO the code looks unnecessarily over complicated.

Swagger doesn't read Path parameter: #Path("folder{path:.*}")

I have a problem getting Swagger to generate correct documentation for an API call that has an optional path parameter.
I'm building an API that peers into a hierarchical structure, similar to a file system. I want to call the same method to get the root structure as I do to get a sub resource. E.g:
Get the root: /folder
Get a sub folder: /folder/path/to
My Jax-rs method looks like this:
#GET #Path("folder{path:.*}")
Response folderContents(#ApiParam(value = "The folder to list", required = false) #PathParam("path") String path)
{...}
My method call works, but my swagger documentation is incorrect and doesn't work. Swagger-ui generates GET calls that look like this when I run it:
http://localhost:8080/storage-war/rest/filestore/folder{path:.*}
I'm looking for a way to either force Swagger to generate the correct signature or rebuild my regular expression so that my generated Swagger is correct.
Previously I'v tried using #Path("folder/{path:.}")*; his generated correct Swagger documentation but didn't match my no path given case. I've also tried #Path("/folder{p:/?}{path:(.)}")*; This produced a working method call but incorrect Swagger docs.
Is there a straightforward way to do what I'm looking for?
Edit:
In the end I created separate method calls for root and folders. Then I decorated the root call with it with #ApiOperation(hidden = true). This way I have an extra method in my code but only one method show up in my Swagger docs.
#GET #Path("folder/{path:.*}")
Response folderContents(#PathParam("path") String path)
{...}
#GET #Path("folder")
#ApiOperation(hidden = true)
Response rootContents()
{...}
In swagger, path parameters are always required. Understanding that in many frameworks and in practice they can be optional, but in the swagger definition they are required. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields-7

Jenkins: Using validateButton on a class without Descriptor

I have a hudson.model.ManagementLink (which creates a page which can be accessed via Manage Jenkins.
I have some form fields which I'd like to validate using the f:validateButton. If this were to be done on say the config.jelly page of a Publisher (which has a descriptor) a simple doXXX() method would work in the descriptor.
If I add a similar doXXX() method directly to hudson.model.ManagementLink it is in fact accessible directly via the URL myPage/myMethod however the f:validateButton just returns a 404 and obviously I need to send the form values.
One potential solution I've come accross is withCustomDescriptorByName and have the logic in a descriptor I have elsewhere, but I can't get that to work.
To answer the actual question above (i.e. no descriptor), you can supply any fields needed for validation to the with attribute of f:validateButton (comma separated) then set the method attribute to something like the following...
method="${it.fullURL}triggerOfficial"
... then in the java (it) there's a method...
public String getFullURL(){
return Stapler.getCurrentRequest().getOriginalRequestURI().substring(1);
}
...then also the method to perform the validation itself...
public FormValidation doTriggerOfficial() {
return FormValidation.ok("hello");
}
However, if you want to perform normal field validation (without a validateButton) on a class type which doesn't normally have a descriptor...
1) Add implements Describable<YourClassName> to your class signature
2) Add something like this...
#SuppressWarnings("unchecked")
#Override
public Descriptor<ConfigLink> getDescriptor() {
Jenkins jenkins = Jenkins.getInstance();
if (jenkins == null) {
throw new IllegalStateException("Jenkins has not been started");
}
return jenkins.getDescriptorOrDie(getClass());
}
3) Plus an inner class (with the normal doCheckXXX methods)
#Extension
public static final class DescriptorImpl extends Descriptor<YourClassName> {...}
4) Then finally to link it in the jelly add attribute descriptor="${it.descriptor}" to the f:form tag containing your form elements you want to have auto-validated (this will invoke the getDescriptor detailed in step 2)

RESTful: how to dynamically extend an API path's (or available resources)?

I've had a nice 'ride' with RESTful technology. I am using a Hello.java resource like this:
#Path("/hello")
public class Hello {
... /* GET/PUT/POST */
}
With this I can access my resource with the path http://my.host/res/hello . I want to 'ride' RESTful even harder. Having this one resource path is a bit boring.
PROBLEM
I would like to have a dynamically created resources like this:
http://my.host/res/hello
http://my.host/res/hello/1
http://my.host/res/hello/2
...
http://my.host/res/hello/999
It doesn't make sense to create a .java resource for every #Path("/hello/1") ... #Path("/hello/999"). Right? Probably this list of sub-resources could be even bigger or dynamically change in time. What is the solution for that?
Thanks.
You can use the #Path annotation on methods inside your Resource class.
#Path("/hello")
public class Hello {
... /* GET/PUT/POST */
#GET
#Path("{id}")
public String myMethod(#PathParam("id") String id) {...}
}
The paths will be concatenated so it will match /hello/13. The {id} is a placeholder for the actual value entered, which can be retrieved with #PathParam. In the previous URI, the String id will have the value 13.
You will have to use PathParam feature for REST URI. http://docs.oracle.com/javaee/6/api/javax/ws/rs/PathParam.html
#Path("/hello/{id}")
public class Hello {
}

Rest set #Path for resource updates

Using Jersey, what is the RESTish way to this, Should I create a method starting with "update" like this, or I should create a subresource (or whatever Jax-Rs thing) under accountseetings path? Or should I simply use the same method name with different verbs?
#GET
#Path("/accountsettings")
public Settings accountSettings() {
}
#PUT
#Path("/updateaccountsettings")
public void updateAccountSettings() {
}
In REST verbs define what you are doing and URLs define what you are doing it to.
So here a PUT to /accountsettings would seem to be the normal way to do it.
Calling the method updateAccountSettings() seems to make sense.

Categories