JAX-RS: OPTIONS for every Resource - java

I am using a JAX-RS interface with XMLHttpRequest (XHR). Due to the XHR preflight, XHR send always OPTIONS before calling the real resource.
Now I have dozens of methods and I need the OPTIONS for every resoruce. Is there any way to do this automatically? I dont want to write dozens of methods like:
#OPTIONS
#Path("/{id}")
#PermitAll
public Response optionsById() {
return Response.status(Response.Status.NO_CONTENT).build();
}
#OPTIONS
#Path("/{id}/data")
#PermitAll
public Response optionsByData() {
return Response.status(Response.Status.NO_CONTENT).build();
}

UPDATE 09/12/2013: THIS DOES NOT WORK. Using this all #GET/#DELETE/#POST/#PUT are not working any more.
Finally I solved my problem. I created a super class OptionsResource, from which all resources inherit. This resoruce contains:
// Match root-resources
#OPTIONS
#PermitAll
public Response options() {
return Response.status(Response.Status.NO_CONTENT).build();
}
// Match sub-resources
#OPTIONS
#Path("{path:.*}")
#PermitAll
public Response optionsAll(#PathParam("path") String path) {
return Response.status(Response.Status.NO_CONTENT).build();
}
An example:
#Path("/test")
public class TestResource extends OptionsResource {
#GET
#Produces("text/plain;charset=UTF-8")
public Response index() {
return Response.status(Status.OK).entity("works").build();
}
}
This matches:
curl -I -X OPTIONS http://myhost.com/test
curl -I -X OPTIONS http://myhost.com/test/asd/aasd/12/
etc.

Quite a late reply, but a much nicer solution is to use a filter that catches all the OPTIONS call before path matching. In Kotlin, it will look like this:
#Provider #PreMatching
class OptionsFilter: ContainerRequestFilter {
override fun filter(requestContext: ContainerRequestContext) {
if (requestContext.method == "OPTIONS") {
requestContext.abortWith(Response.status(Response.Status.NO_CONTENT).build())
}
}
}

The java version:
#Provider
#PreMatching
public class OptionFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (requestContext.getMethod().contentEquals("OPTIONS")) {
requestContext.abortWith(Response.status(Response.Status.NO_CONTENT).build());
}
}
}

Related

How to logout programmatically from magnolia cms

I'd like to create custom REST for logging out users. I created jax-rs based endpoint definition with one method /logout:
#Path("/test")
public class MyEndpoint<D extends EndpointDefinition> extends AbstractEndpoint<D> {
#Path("/logout")
#GET
#Produces(MediaType.APPLICATION_JSON)
public void logout() {
//how to logout user here?
}
}
What code should I put in place of //how to logout user here? to make it work?
You can inject the following component and trigger logout from it.
info.magnolia.context.UserContext
I created working solution based on info.magnolia.cms.security.LogoutFilter
#Path("/logout")
#GET
#Produces(MediaType.APPLICATION_JSON)
public void logout(#Context HttpServletRequest request) {
info.magnolia.context.Context ctx = MgnlContext.getInstance();
if (ctx instanceof UserContext) {
AuditLoggingUtil.log((UserContext) ctx);
((UserContext) ctx).logout();
}
if (request.getSession(false) != null) {
request.getSession().invalidate();
}
}

how to inject headers in a `#context HttpServletRequest`?

Let's say I have this code:
#ApplicationPath("...")
public class MyApp extends ResourceConfig {
public SalesLayerApplication() {
this.register(HeaderInjecterFilter.class);
this.register(Test.class);
}
}
#PreMatching
public class HeaderInjecterFilter implements ContainerRequestFilter {
#Override
public void filter(final ContainerRequestContext crc) throws IOException {
crc.getHeaders().add("foo", "bar");
}
}
#Path("/test")
public class Test {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String dump(#Context final HttpServletRequest request) {
return request.getHeader("foo");
}
}
I was expecting to call the rest entry point /test and to retrieve the string bar.
But all I see is null
If I use #HeaderParam("foo") I correctly retrieve the variable, but I need to access throug the #Context HttpServletRequest.
Why would you expect that adding headers to the ContainerRequestContext would also add it to the HttpServletRequest? These are completely unrelated entities. Try injecting HttpHeaders or you can also inject the ContainerRequestContext directly.

URI Recognition in Java Using JAX-RS #Path Annotations at Class and Method Level

I've created a situation in a Resource class where I'm receiving 404s for all 3 endpoints and was hoping someone could help explain why. Here is my resource class:
#Path("/incidents")
public class RegistrationResource {
#GET
#Path("/{incidentId}/registration")
public Response getRegisteredIncidents(#PathParam("incidentId") String incidentId) {
...
}
#GET
#Path("/registration/download")
public Response downloadRegistrationIncidentsReport() {
...
}
#GET
#Path("/registration/email")
public Response emailRegistrationIncidentsReport() {
...
}
}
I'm also using the JAX-RS Application class to register my resources and set a base "context" path across all of the endpoints:
#ApplicationPath("/context")
public class AdminApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(RegistrationResource.class);
...
return resources;
}
}
Now if I split these methods into more than one resource class I can get each service to work fine, but my question is why won't the design above work? The full endpoints I'm trying to call here in order of the methods above are:
/context/incidents/55/registration
/context/incidents/registration/download
/context/incidents/registration/email
Why are the mappings above not being found? With this design I'm getting a 404 for each of the endpoints.
org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (404 - Not Found) with message 'null' while processing GET request sent to http://localhost:9081/someapp/context/incidents/55/registration
As a further point of clarification I'm using Wink 1.1 as my JAX-RS implementation since I'm bound to the packaged version that comes with WebSphere 8.5.5.5 at this time.
Thanks for your time!
-----------UPDATE #1-----------
In light of the comments below I have modified some of the endpoints. However I am still receiving random failures on certain endpoints with either 404 or, interestingly, 405 errors. It's important to note that if I redeploy sometimes different endpoints will fail, or perhaps all will work. Then if I redeploy again I will get similar, but different results of certain endpoints failing or all working. The bottom line is that it is very inconsistent.
Because of that it seems as if more endpoints are conflicting than in my original post so I am pasting all of my endpoints here for the sake of completeness.
/* Public Service Set */
#ApplicationPath("/public")
public class PublicApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(IncidentResource.class);
resources.add(IncidentDetailsResource.class);
resources.add(TicketResource.class);
resources.add(RegistrationResource.class);
return resources;
}
}
#Path("/incidents")
public class IncidentResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidents() throws Exception {
//...
}
#Path("/")
public class IncidentDetailsResource {
#GET
#Path("/v1/incidents/{incidentId}/details")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidentDetails(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
#GET
#Path("/v2/incidents/{incidentId}/details")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidentDetailsAsJson(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/incidents/{incidentId}/tickets")
public class TicketResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getTickets(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/incidents")
public class RegistrationResource {
#POST
#Path("/{incidentId}/registration")
#Consumes(MediaType.APPLICATION_JSON)
public Response register(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRegistrationRequest> baseRequestMessage) throws Exception {
//...
}
}
/* Admin Service Set */
#ApplicationPath("/admin")
public class AdminApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(AdminIncidentResource.class);
resources.add(AdminIncidentDetailsResource.class);
resources.add(AdminRegistrationResource.class);
resources.add(AdminRegistrationReportResource.class);
resources.add(AdminTicketResource.class);
return resources;
}
}
#Path("/incidents")
public class AdminIncidentResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response addIncident(BaseRequestMessage<IncidentRequest> baseRequestMessage) throws Exception {
//...
}
#PUT
#Path("/{incidentId}")
#Produces(MediaType.APPLICATION_JSON)
public Response updateIncident(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRequest> baseRequestMessage) throws Exception {
//...
}
#DELETE
#Path("/{incidentId}")
#Produces(MediaType.APPLICATION_JSON)
public Response deleteIncident(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/")
public class AdminIncidentDetailsResource {
#PUT
#Path("/v1/incidents/{incidentId}/details")
#Consumes(MediaType.APPLICATION_JSON)
public Response updateIncidentDetails(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentDetailRequest> baseRequestMessage) throws Exception {
//...
}
#PUT
#Path("/v2/incidents/{incidentId}/details")
#Consumes(MediaType.APPLICATION_JSON)
public Response updateIncidentDetailsAsJson(#PathParam("incidentId") String incidentId, BaseRequestMessage<JsonNode> baseRequestMessage) throws Exception {
//...
}
}
#Path("/incidents/{incidentId}/tickets")
public class AdminTicketResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response addTicket(#PathParam("incidentId") String incidentId, BaseRequestMessage<TicketRequest> baseRequestMessage) throws Exception {
//...
}
#PUT
#Consumes(MediaType.APPLICATION_JSON)
public Response updateTicket(#PathParam("incidentId") String incidentId, BaseRequestMessage<TicketRequest> baseRequestMessage) throws Exception {
//...
}
#DELETE
#Path("/{detailsUrn}")
#Consumes(MediaType.APPLICATION_JSON)
public Response removeTicket(#PathParam("incidentId") String incidentId, #PathParam("detailsUrn") String detailsUrn) throws Exception {
//...
}
}
#Path("/incidents/registration/report")
public class AdminRegistrationReportResource {
#GET
#Path("/download")
#Produces("application/vnd.ms-excel")
public Response downloadRegistrationReport() throws Exception {
//...
}
#GET
#Path("/email")
#Produces(MediaType.APPLICATION_JSON)
public Response emailRegistrationReport() throws Exception {
//...
}
}
#Path("/incidents/{incidentId}/registration")
public class AdminRegistrationResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getRegisteredUsers(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
#PUT
#Consumes(MediaType.APPLICATION_JSON)
public Response updateRegisteredUser(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRegistrationRequest> baseRequestMessage) throws Exception {
//...
}
}
For sake of expediency...the code above ends up generating these uris:
GET /public/incidents
GET /public/v1/incidents/{incidentId}/details
GET /public/v2/incidents/{incidentId}/details
GET /public/incidents/{incidentId}/tickets
POST /public/incidents/{incidentId}/registration
POST /admin/incidents
PUT /admin/incidents/{incidentId}
DELETE /admin/incidents/{incidentId}
PUT /admin/v1/incidents/{incidentId}/details
PUT /admin/v2/incidents/{incidentId}/details
POST /admin/incidents/{incidentId}/tickets
PUT /admin/incidents/{incidentId}/tickets
DELETE /admin/incidents/{incidentId}/tickets/{detailsUrn}
GET /admin/incidents/registration/report/download
GET /admin/incidents/registration/report/email
GET /admin/incidents/{incidentId}/registration
PUT /admin/incidents/{incidentId}/registration
That's a lot of code; I wanted to avoid that originally but it seems it is necessary. Also, for what it is worth, things seemed to be working until I added the AdminRegistrationResource class. Perhaps something with one of the endpoints in that class began causing a conflict?
Again...thanks for your time!
-----------UPDATE #2-----------
I'd thought I'd post one of my recent specific errors here in the hopes that it may help in solving the issue. When I call this endpoint:
GET /public/incidents
I'm receiving this error message:
00000203 ResourceRegis I org.apache.wink.server.internal.registry.ResourceRegistry filterDispatchMethods The system cannot find any method in the com.somewhere.unimportant.rest.resource.external.RegistrationResource class that supports GET. Verify that a method exists.
00000203 RequestProces I org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (405) with message 'null' while processing GET request sent to https://test.somewhere.com:88888/app777/public/events
Now what's even more interesting about this is that I receive this error locally, but I've also pushed my code up to a test environment where there are two load-balanced servers. In the test environment I receive this error on one of the servers consistently but the other one works consistently. So a 50% failure rate split on one successful server and one unsuccessful server.
Strange...why is it looking in that class for the endpoint I'm hitting?
-----------UPDATE #3-----------
Ok, I've taken out the use of the #ApplicationPath annotation in my PublicApplication class completely (as suggested from the comments below) and replaced it with a mere "/" but still receive a 404 error in the following scenario (note that for this test I have completely removed the "admin" services):
/* Public (and only) Service Set */
#ApplicationPath("/")
public class PublicApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(IncidentResource.class);
resources.add(IncidentDetailsResource.class);
resources.add(TicketResource.class);
resources.add(RegistrationResource.class);
return resources;
}
}
#Path("/public")
public class IncidentResource {
#GET
#Path("/incidents")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidents() throws Exception {
//...
}
#Path("/public")
public class IncidentDetailsResource {
#GET
#Path("/v1/incidents/{incidentId}/details")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidentDetails(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
#GET
#Path("/v2/incidents/{incidentId}/details")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidentDetailsAsJson(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/public/incidents/{incidentId}/tickets")
public class TicketResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getTickets(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/public/incidents/{incidentId}/registration")
public class RegistrationResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response register(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRegistrationRequest> baseRequestMessage) throws Exception {
//...
}
}
In this scenario I am getting this error:
org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (404 - Not Found) with message 'null' while processing GET request sent to http://test.somewhere.com:88888/app777/public/events
when trying to call:
GET /public/incidents
It may also be relevant that the other 4 endpoints above work just fine. And, more importantly, if I change the IncidentResource class to look like the following (moving the #Path annotation at the method level completely to the class level) then all 5 endpoints work:
#Path("/public/incidents")
public class IncidentResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidents() throws Exception {
//...
}
I appreciate everyone who has stayed with me this far!
Remove #ApplicationPath("/context") or alternatively don't use the /context in the request uri or just use #ApplicationPath("/") it wont break even when you upgrade.
All the above different ways should work, but for your version I would recommend #ApplicationPath("/") and update your request uris by removing /context and everything should work the way you originally intended.
Apache Wink doesn't have support for ApplicationPath just yet.
https://issues.apache.org/jira/browse/WINK-398
This might work for you:
#Path("/incidents/{incidentId}/registration")
public class RegistrationResource1 {
#GET
public Response getRegisteredIncidents(#PathParam("incidentId") String incidentId) {
...
}
}
#Path("/incidents/registration/download")
public class RegistrationResource {
#GET
public Response downloadRegistrationIncidentsReport() {
...
}
}
However, I would not recommend splitting up the controller like that. Instead, just make a point of removing as much of the code inside each function to separate business logic classes.

How to Optionally Protect a Resource with Custom Dropwizard Filter

I'm using Dropwizard 0.9.2 and I want to create a resource that requires no authentication for GET and requires basic authentication for POST.
I have tried
#Path("/protectedPing")
#Produces(MediaType.TEXT_PLAIN)
public class ProtectedPing {
#GET
public String everybody() {
return "pingpong";
}
#PermitAll
#POST
public String authenticated(){
return "secret pingpong";
}
with
CachingAuthenticator<BasicCredentials, User> ca = new CachingAuthenticator<>(environment.metrics(), ldapAuthenticator, cbSpec);
AdminAuthorizer authorizer = new AdminAuthorizer();
BasicCredentialAuthFilter<User> bcaf = new BasicCredentialAuthFilter.Builder<User>().setAuthenticator(ca).setRealm("test-oauth").setAuthorizer(authorizer).buildAuthFilter();
environment.jersey().register(bcaf);
environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
environment.jersey().register(new ProtectedPing());
This seems to result in all requests to "/protectedPing" requiring basic auth.
In Dropwizard 0.9.2 the documentation says to create a custom filter if I have a resource that is optionally protected. I'm assuming I need to do that, but I don't know where to start, or if that I what I actually need to do.
this is more of a jersey problem than a dropwizard problem. You can have a look here: https://jersey.java.net/documentation/latest/filters-and-interceptors.html
Essentially what you want is:
Create an annotation that indicates that you want to test for authentication (e.g. #AuthenticatePost)
Create the resource and annotate the correct method with #AuthenticatePost
Create your authentication filter (probably kind of like what you did above).
In the dynamic feature, test for the annotation to be present on the passed in resource. This will hold true for post, false for get. Then register the AuthenticationFilter directly on the resource method instead of globally on the resource.
This would be a semi-complete example of how I would solve this:
public class MyDynamicFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if(resourceInfo.getResourceMethod().getAnnotation(AuthenticateMe.class) != null ) {
context.register(MyAuthFilter.class);
}
}
public class MyAuthFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// do authentication here
}
}
public #interface AuthenticateMe {
}
#Path("myPath")
public class MyResource {
#GET
public String get() {
return "get-method";
}
#POST
#AuthenticateMe
public String post() {
return "post-method";
}
}
}
Note, the DynamicFeature checks that the Authenticate Annotation is present, before registering the authentication with the feature context.
I hope that helps,
let me know if you have any questions.

JAX-RS: Multiple paths

Is it possible to do something like that?
import javax.ws.rs.GET;
import javax.ws.rs.Path;
public class xxx
{
#GET
#Path(value = "path1")
public Response m1()
{
...
}
#GET
#Path(value = "path2")
public Response m1()
{
...
}
}
I'm using RESTEasy btw.
#Path("/{a:path1|path2}")
From resteasy docs:
http://docs.jboss.org/resteasy/docs/1.0.2.GA/userguide/html_single/index.html#_Path_and_regular_expression_mappings
yes you can do that although you will have to rename your methods so that their signature is different.
Update: Check Dieter Cailliau's answer, #Path("/{a:path1|path2}") is probably what you want...
public class BlahResource{
#GET
#Path("path1")
public Response m1(){
return Response.ok("blah").build();
}
#GET
#Path("path2")
public Response m2(){
return this.m1();
}
you can check JSR-311's API and it's reference implementation named "jersey" there:
JSR311 API
Jersey
Some extra details about Path annotation...
As a previous responses state, regular expressions to be used with in the annotated path declaration mapping:
{" variable-name [ ":" regular-expression ] "}
You can declare multiple paths, but there is also a path hierarchy that was not immediately obvious to me whereby the class annotated path prefixes the following method path annotations. One might write the following class for a concise multiple path option which could be useful for resource versioning perhaps.
#Path("/{a:v1|v2}")
#Produces("text/*")
public class BlahResource {
#GET
#Path("/blah")
public Response m1() {
return Response.ok("blah").build();
}
}
Please note the fact that the class "BlahResource" has been declared with the path "/v1" or "/v2" making the resource accessible as:
$ curl localhost:8080/v1/blah
blah
and also
$ curl localhost:8080/v2/blah
blah
You could use sub resources to map two paths to the same resource:
public class MySubResource {
#GET
public Response m1() {
return Response.ok("blah").build();
}
}
#Path("/root")
public class MyRootResource {
#Path("/path1")
public MySubResource path1() {
return new MySubResource();
}
#Path("/path2")
public MySubResource path2() {
return new MySubResource();
}
}

Categories