Overriding specific resource in REST API - java

I have a particular scenario where I have a rest resource having some methods with their path.
My requirement is to create a new resource which will extend the above resource and I want to override only a specific method and want to call rest methods from the parent API only.
For example -
#Path("/data")
public class AResource {
#GET
#Path("/login")
public Response login()
{
//login code
}
#GET
#Path("/logout")
public Response logout()
{
//logout code
}
}
#Path("/data")
public class BResource extends AResource {
#GET
#Path("/login")
#Override
public Response login()
{
//login code modified as per requirement
}
}
I tried this, But its still calling parent class methods only for both the methods.
Is there any alternative to this, please suggest.
Thanks in advance!!!!!!!!!!!

Related

Get request body in failed access of method with POST annotation

I have a RESTful web service with an #POST annotated method that throws an exception when the request content type cannot be consumed. I would like to see what exactly went wrong. For this I would like to access all the details of the failed POST call, such as the body content. For this I created an #Provider catching NotSupportedException. I still have problems finding a way to get the desired details though. How would I do that?
The #POST annotated method:
#Path("/language")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class LanguageResource {
#POST
public Response postLanguages(Session session) {
return Response.status(Response.Status.OK)
.entity(Mock.getLanguages())
.build();
}
}
The #Provider:
#Provider
public class NotSupportedMapper implements ExceptionMapper<NotSupportedException> {
#Override
public Response toResponse(NotSupportedException exception) {
System.out.println(exception.toString());
return Response.status(Response.Status.NOT_IMPLEMENTED)
.build();
}
}

Restlet path param does not work

Below is my routing
public Restlet createInboundRoot(){
Router router = new Router(getContext());
router.attach("account/profile",UserProfile.class);
Following is the Resource class UserProfile.java
#post
#path("add")
public void addUser(User user){
#post
#path("modify")
public void modifyUser(User user){
#post
public void test(){//only this is called
I want to invoke one resource class and do couple of identical functions for a resource class. That means, my above resource class handles functions related to the UserProfiles such as add, modify.
URL are:
account/profile/add => to add a user
account/profile/modify => to modify a user
anyway, above my implementation doesn't work as only the test() method can be invoked through account/profile/
I tried with Pathparams as well.But it also didnot work.
For path params:
router.attach("account/profile/{action}",UserProfile.class);
was added and in the resource class,
#post
#path("{action}")
public void addUser(#pathparam("action") String action, User user){
Anyone tell me where is my problem.
The way you attach your UserProfile server resource is a bit strange. I think that you mixed the native routing of Restlet and the one from the JAXRS extension.
I made some tests regarding your use case and I was able to have the behavior you expect. I used the version 2.3.5 of Restlet.
Here is what I did:
Since you want to use JAXRS, you need to create a JaxRsApplication and attach it on the component:
Component component = new Component();
component.getServers().add(Protocol.HTTP, 8182);
// JAXRS application
JaxRsApplication application
= new JaxRsApplication(component.getContext());
application.add(new MyApplication());
// Attachment
component.getDefaultHost().attachDefault(application);
// Start
component.start();
The application simply list the server resources you want to use but doesn't define routing and paths:
import javax.ws.rs.core.Application;
public class MyApplication extends Application {
public Set<Class<?>> getClasses() {
Set<Class<?>> rrcs = new HashSet<Class<?>>();
rrcs.add(AccountProfileServerResource.class);
return rrcs;
}
}
The server resource defines handling methods and associated routes:
import javax.ws.rs.POST;
import javax.ws.rs.Path;
#Path("account/profile/")
public class AccountProfileServerResource {
#POST
#Path("add")
public User addUser(User user) {
System.out.println(">> addUser");
return user;
}
#POST
#Path("modify")
public User modifyUser(User user) {
System.out.println(">> modifyUser");
return user;
}
#POST
public void test() {
System.out.println(">> test");
}
}
When I call the different paths, right methods are called:
http://localhost:8182/account/profile/modify: the modifyUser method is called
http://localhost:8182/account/profile/add: the addUser method is called
http://localhost:8182/account/profile/: the test method is called
Hope it helps you,
Thierry

Resteasy - Override Method with generics

Right now i am working with resteasy. I have build a little Rest Application with the following struktur:
Controller Class:
#PATH("/rest")
public abstract class Controller<T>
{
#POST
public Response post(T ressource){[..]}
#PUT
public Response put(T ressource){[..]}
#DELETE
#Path("/{id}")
public Response delete(#PathParam(value = "id") int id)
#GET
public Response get(){[..]}
}
ConrollerA Class (This is only an Example):
PATH("/rest/A")
public class ControllerA extends Controller<RessourceA>
{
#Override
public Response post(RessourceA ressource){[..]}
[..]
#Override
public Response get(){[..]}
}
I start the server with jetty right now and everytime i make a POST Request to "/rest/A", the Server tells me, that there is no such a method but if i make a GET Requst to the Path it all works.
Is there a Problem with Jetty or Java if i override a Method with generic typs?
If i write the "#POST" annotation above the method it works, but i do not want this kind of solution. Do i have to make some settings in the web.xml or is the problem with java?

jersey 2: How to create custom HTTP param binding

I am trying to create a custom http param binding for my restful service. Please see the example below.
#POST
#Path("/user/{userId}/orders")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public MyResult foo(#PathParam("userId") String someString, #UserAuthHeaderParam String authString){
}
You can see that there is a UserAuthHeaderParam annotation in the function signature. What I want to do is have a custom http param binding other than the standard javax.ws.rs.*Param .
I have try to implement org.glassfish.hk2.api.InjectionResolver which basically extract the value from http header:
public class ProtoInjectionResolver implements InjectionResolver<UserAuthHeaderParam>{
...
#Override
public Object resolve(Injectee injectee, ServiceHandle< ? > root)
{
return "Hello World";
}
...
}
When I call the restful service, the server get below exceptions. It indicates that the framework fails to resolve the param in the function signature:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=String,parent=MyResource,qualifiers={}),position=0,optional=false,self=false,unqualified=null,2136594195),
java.lang.IllegalArgumentException: While attempting to resolve the dependencies of rs.server.MyResource errors were found
Please help. Any advise is appreciated. I do make a lot of search on google but fails to make it work. Jersey 2.*. How to replace InjectableProvider and AbstractHttpContextInjectable of Jersey 1.* might be the similar question.
-- UPDATES:
I use AbstractBinder to bind my resolver to UserAuthHeaderParam:
public class MyApplication extends ResourceConfig
{
public MyApplication()
{
register(new AbstractBinder()
{
#Override
protected void configure()
{
// bindFactory(UrlStringFactory.class).to(String.class);
bind(UrlStringInjectResolver.class).to(new TypeLiteral<InjectionResolver<UrlInject>>()
{
}).in(Singleton.class);
}
});
packages("rs");
}
}
Thank you!
If all you want is to pass value directly from the header to the method you don't need to create custom annotations. Let's say you have a header Authorization, then you can easily access it by declaring your method like this:
#GET
public String authFromHeader(#HeaderParam("Authorization") String authorization) {
return "Header Value: " + authorization + "\n";
}
You can test it by calling curl, e.g.
$ curl --header "Authorization: 1234" http://localhost:8080/rest/resource
Header Value: 1234
Given that the answer to your question, how to create custom binding is as follows.
First you have to declare your annotation like this:
#java.lang.annotation.Target(PARAMETER)
#java.lang.annotation.Retention(RUNTIME)
#java.lang.annotation.Documented
public #interface UserAuthHeaderParam {
}
Having your annotation declared you have to define how it will be resolved. Declare the Value Factory Provider (this is where you'll have access to the header parameters - see my comment):
#Singleton
public class UserAuthHeaderParamValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
protected UserAuthHeaderParamValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator locator) {
super(mpep, locator, Parameter.Source.UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(Parameter parameter) {
Class<?> classType = parameter.getRawType();
if (classType == null || (!classType.equals(String.class))) {
return null;
}
return new AbstractHttpContextValueFactory<String>() {
#Override
protected String get(HttpContext httpContext) {
// you can get the header value here
return "testString";
}
};
}
}
Now declare an injection resolver
public class UserAuthHeaderParamResolver extends ParamInjectionResolver<UserAuthHeaderParam> {
public UserAuthHeaderParamResolver() {
super(UserAuthHeaderParamValueFactoryProvider.class);
}
}
and a Binder for your configuration
public class HeaderParamResolverBinder extends AbstractBinder {
#Override
protected void configure() {
bind(UserAuthHeaderParamValueFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(UserAuthHeaderParamResolver.class)
.to(new TypeLiteral<InjectionResolver<UserAuthHeaderParam>>() {})
.in(Singleton.class);
}
}
now the last thing, in your ResourceConfig add register(new HeaderParamResolverBinder()), like this
#ApplicationPath("rest")
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new HeaderParamResolverBinder());
packages("your.packages");
}
}
Given that, you should be now able to use the value as you wanted:
#GET
public String getResult(#UserAuthHeaderParam String param) {
return "RESULT: " + param;
}
I hope this helps.
I don't know how to resolve your exception. However, may I propose you a different way to do the same thing. I hope it helps.
I've faced exactly the same problem: I need extra parameters in the http header (btw, also related to authentication). Besides, I need to send them in every call, since I want to do a "typical" rest implementation, without maintaining a session.
I'm using Jersey 2.7 - but I'd say it should work in 2.0. I've followed their documentation
https://jersey.java.net/documentation/2.0/filters-and-interceptors.html
It's quite clear there, but anyway I copy-paste my implementation below.
It works fine. True there are some other ways to secure a rest service, for example this is a good one:
http://www.objecthunter.net/tinybo/blog/articles/89
But they depend on the application server implementation and the database you use. The filter, in my opinion, is more flexible and easier to implement.
The copy-paste: I've defined a filter for authentication, which applies to every call and it is executed before the service (thanks to #PreMatching).
#PreMatching
public class AuthenticationRequestFilter implements ContainerRequestFilter {
#Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
final MultivaluedMap<String, String> headers = requestContext.getHeaders();
if (headers == null) {
throw new...
}
// here I get parameters from the header, via headers.get("parameter_name")
// In particular, I get the profile, which I plan to use as a Jersey role
// then I authenticate
// finally, I inform the Principal and the role in the SecurityContext object, so that I can use #RolesAllowed later
requestContext.setSecurityContext(new SecurityContext() {
#Override
public boolean isUserInRole(final String arg0) {
//...
}
#Override
public boolean isSecure() {
//...
}
#Override
public Principal getUserPrincipal() {
//...
}
#Override
public String getAuthenticationScheme() {
//...
}
});
}
}
You have to include this filter class in your implementation of ResourceConfig,
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
// my init
// my packages
register(AuthenticationRequestFilter.class); // filtro de autenticación
// other register
}
}
Hope it helps!
If your need is to retrieve all the http headers binding into one object, a solution could be to use the #Context annotation to get javax.ws.rs.core.HttpHeaders; which contains the list of all request headers.
#POST
#Path("/user/{userId}/orders")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public MyResult foo(#PathParam("userId") String someString, #Context HttpHeaders headers){
// You can list all available HTTP request headers via following code :
for(String header : headers.getRequestHeaders().keySet()){
System.out.println(header);
}
}
here is my actual implementatipn of UserAuthHeaderParamValueFactoryProvider class
import javax.inject.Inject;
import javax.inject.Singleton;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
import org.glassfish.jersey.server.model.Parameter;
#Singleton
public class UserAuthHeaderParamValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
protected UserAuthHeaderParamValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator locator) {
super(mpep, locator, Parameter.Source.UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(Parameter parameter) {
Class<?> classType = parameter.getRawType();
if (classType == null || (!classType.equals(String.class))) {
return null;
}
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
//you can use get any header value.
return getContainerRequest().getHeaderString("Authorization");
}
};
}

JAX-RS custom SecurityContext leads to wrong error code in Jersey

I followed the Jersey tutorials to implement a completely custom SecurityContext for my application. I created a custom ContainerRequestFilter to set the SecurityContext as follows:
package com.my.security;
#Provider
#Priority(Priorities.AUTHORIZATION)
public class SecurityRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) {
requestContext.setSecurityContext(new MySecurityContext(requestContext));
}
public static final class MySecurityContext implements SecurityContext {
private String token;
public MySecurityContext(ContainerRequestContext requestContext) {
token = requestContext.getHeaderString("token");
}
#Override
public boolean isUserInRole(String role) {
return role.equals("admin") && token.equals("token-for-admin");
}
// other methods omitted
}
}
The logic in the isUserInRole method is irrelevant, it's just a mock to make the point.
My endpoint looks something like:
package com.my.rest;
#PermitAll
#Path("/people")
public class PeopleRestService {
#RolesAllowed({"admin"})
#Path("/{email}")
#DELETE
public Response deletePerson(#PathParam("email") final String email) {
peopleService.removePerson(email);
return Response.ok().build();
}
}
Now I created a test (using JerseyTest) configured with the packages where the two classes are:
#Override
protected Application configure() {
return new ResourceConfig().packages("com.my.rest", "com.my.security");
}
If I execute the following in my test:
Response response = target("people/my#email.com")
.request().header("token", "token-for-admin").delete();
Assert.assertEquals(200, response.getStatus());
everything works fine.
However, if I execute the following:
Response response = target("people/my#email.com").request().delete();
Assert.assertEquals(403, response.getStatus());
I would expect a 403 error code because I didn't set the authentication token. However, I get a 500 error code and a Grizzly (the container used for the test) HTML response with the string "Request failed.".
If I comment out the #Provider annotation on the SecurityRequestFilter class or remove the package com.my.security from the test configuration, Jersey uses the container provided SecurityContext and correctly returns a 403 instead.
Why is this happening? Shouldn't Jersey return a 403 with a custom SecurityContext too? What am I missing?
I apologize for my dumbness. the logic in the isUserInRole method is completely relevant! I had a NPE in there that I didn't see and was causing the 500.

Categories

Resources