REST JAX RS : Jersey : How to read pathparam of root resource in sub resource? - rest

I am trying to read the root resource path param in a sub resource file, but I am getting an error. Please Help me.
The way I am following is :
Root resource Service:
#Path("/{messageId}/comments")
public CommentResource getCommentResources(){
return new CommentResource();
}
Sub resource code:
#Path("/")
public class CommentResource {
private CommentDAOImpl commentDaoObject = new CommentDAOImpl();
#GET
#Produces(MediaType.APPLICATION_JSON)
public ArrayList<Comment> getAllCommentsForAMessage(#PathParam("messageId") long messageId){
return commentDaoObject.getAllCommentsForMessage(messageId);
}
#Path("/{commentId}")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Comment getCommentForAMessage(#PathParam("commentId") long commentId, #PathParam("messageId") long messageId){
return commentDaoObject.getCommentForMessage(messageId, commentId);
}
}
While reading the "messageId" path param in sub resource I am getting the error:
Error: #PathParam value 'messageId' does not match any #Path annotation template parameters of the java method 'getCommentForAMessage' and its enclosing java type
'org.ramesh.jrs.Messenger.resources.CommentResource'.
Can anyone help me resolve the issue?

If you want pass a parameter to a resource class you must use the ResourceContext.initResource method.
This is how to modify your code:
Root resource Service
#Path("/{messageId}/comments")
public CommentResource getCommentResources(#PathParam("messageId") long messageId, #Context ResourceContext resourceContext){
return resourceContext.initResource(new CommentResource(messageId));
}
Sub resource code:
public class CommentResource {
private CommentDAOImpl commentDaoObject = new CommentDAOImpl();
private long messageId;
public CommentResource(long messageId) {
this.messageId = messageId;
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public ArrayList<Comment> getAllCommentsForAMessage(){
return commentDaoObject.getAllCommentsForMessage(messageId);
}
#GET
#Path("/{commentId}")
#Produces(MediaType.APPLICATION_JSON)
public Comment getCommentForAMessage(#PathParam("commentId") long commentId){
return commentDaoObject.getCommentForMessage(messageId, commentId);
}
}

Related

Integration Tests for RESTEasy Endpoint

I want to perform integration tests on my REST endpoint but am running into issues.
Below is my endpoint. NOTE: I cannot change this part of the code.
#Path("/people")
public class PersonResource {
private final PersonService personService;
#Inject
public PersonResource(final PersonService personService) {
this.personService = personService;
}
#GET
#Produces("application/json")
public List<Person> getPersonList() {
return personService.getPersonList();
}
}
From what I've been able to find online, I have the following basic structure for my test.
public class PersonResourceTest {
private Dispatcher dispatcher;
private POJOResourceFactory factory;
#Before
public void setup() {
dispatcher = MockDispatcherFactory.createDispatcher();
factory = new POJOResourceFactory(PersonResource.class);
dispatcher.getRegistry().addResourceFactory(factory);
}
#Test
public void testEndpoint() throws URISyntaxException {
MockHttpRequest request = MockHttpRequest.get("people");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
System.out.print("\n\n\n\n\n" + response.getStatus() + "\n\n\n\n\n");
System.out.print("\n\n\n\n\n" + response.getContentAsString() + "\n\n\n\n\n");
}
}
However, this results in the following error on the last line of the setup method.
java.lang.RuntimeException: RESTEASY003190: Could not find constructor for class: my.path.PersonResource
I explored the Registry API and thought maybe I should have been using addSingletonResource instead, so I changed the last line of setup to dispatcher.getRegistry().addSingletonResource(personResource); and added the following.
#Inject
private PersonResource personResource;
But that results in a NullPointerException on the last line of setup.
The sparse documentation on the mocking isn't very helpful. Can anyone point out where I'm going wrong? Thanks.
You need to do two things
Add a no arguments constructor to your source class:
public PersonResource() {
this(null)
}
In the test class, initialize the PersonResource class with an instance of PersonService class:
dispatcher.getRegistry().addSingletonResource(new PersonResource(new PersonService()));
If needed, the PersonService class can be mocked:
private Dispatcher dispatcher;
#Mock
private PersonService service;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
dispatcher = MockDispatcherFactory.createDispatcher();
PersonResource resource= new PersonResource(service);
ispatcher.getRegistry().addSingletonResource(resource);
}
Hope it helps!

How to get a path name as a parameter in JAX-RS #Path

I want to create a REST service where the parameter is a path with a non-fixed number of terms. For example:
#Path("obj")
public class ObjectResource {
#GET
#Path(???)
public Response getObj(#Param(???) String path) {
....
}
}
If the request URL is like:
http://myhost.xyz/app/obj/var/share/www
The method getObj would get as its path parameter the String
var/share/www
Alternatively it would be OK to get an array (or Collection) with "var" "share" "www" in separate ordered elements. (I would just do a String.split() on the single string anyway.)
Can this be done?
You can try with PathSegment.
#Path("obj")
public class ObjectResource {
#GET
#Path("{var: \\.+}")
public Response getObj(#PathParam("var") final PathSegment path) {
final String value = path.getPath();
}
}
Edit: What actually works is the following:
#GET
#Path("obj")
public class ObjectResource {
#Path("{part: .*}")
#Produces(MediaType.APPLICATION_JSON)
public Response getObj(#PathParam("part") String pathpart) {
// just return the path
return Response.ok(pathpart).build();
}
}

Rest api how to get parameters?

I'm new to rest api.
I need to make an api that takes a string as parameter and then return boolean.
Now my question is how to i pass that string to my api, and then get the string inside my api?
Here's one example that takes a string in parameter and has a default value if the query parameter is not provided:
#Path("business/department/")
public interface DepartmentService {
#GET
#Path("/cs/availability/chat")
#Produces(MediaType.APPLICATION_JSON)
boolean getCustomerServiceAvailability(#QueryParam("type") #DefaultValue("chat") String type);
}
and the implementation class can be anything that implements your interface. In this example, it's a stateless EJB
#Stateless
public class DepartmentServiceImpl implements DepartmentService {
#Context
private HttpServletRequest request;
private static final Logger LOGGER = Logger.getLogger(DepartmentServiceImpl.class.getName());
#Override
public boolean getCustomerServiceAvailability(String scheduleType) {
RequestInfo reqInfo = new RequestInfo(request, this.getClass(), "getCustomerServiceAvailability");
boolean available;
try {
available = CallBusinessService(scheduleType);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, e.getLocalizedMessage());
throw new ServiceException();
} finally {
reqInfo.logExecutionTime();
}
}
}

Problems when using EntityFilteringFeature and SelectableEntityFilteringFeature with Jersey 2

I'm new to Jersey 2 and JAX-RS, so probably I'm missing something.
What I'm trying to do is a test program to define a coding style in rest services developing.
The test was written in JAVA and uses JERSEY 2.22.2, JDK 1.8.31, MOXY AS JSON Provider.
I defined a Resource with GET methods to support LIST/DETAIL. Due to the size of my POJO, I used some filters and everything was fine.
// 1) First of all I defined the annotation.
#Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#EntityFiltering
public #interface MyDetailView {
public static class Factory extends AnnotationLiteral<MyDetailView>
implements MyDetailView {
private Factory() {
}
public static MyDetailView get() {
return new Factory();
}
}
// 2) Once defined the annotation, I used to
// programmaticaly exclude the list of subItems in the response...
#XmlRootElement
public class MyPojo {
...
//*** THIS SHOULD BE FILTERED IF THE ANNOTATION IS NOT SPECIFIED IN THE RESPONSE ***
#MyDetailView
private List<SubItem> subItems = new ArrayList<SubItem>();
public List<SubItem> getSubItems() {
return subItems;
}
public void setSubItems(List<SubItem> subItems) {
this.subItems = subItems;
}
}
// 3) I registered the EntityFilteringFeature
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
}
// 4) Finally, I wrote the code to include/exclude the subItems
/*
The Resource class has getCollection() and getItem() methods...
getCollection() adds the annotation only if filterStyle="detail"
getItem() always add the annotation
*/
#Path(....)
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class MyResource extends SecuredResource {
//filterStyle -> "detail" means MyDetailAnnotation
#GET
public Response getCollection(
#QueryParam("filterStyle") String filterStyle,
#Context UriInfo uriInfo) {
//THIS CODE AFFECTS THE RESPONSE
boolean detailedResponse = "detail".equals(filterStyle);
Annotation[] responseAnnotations = detailedResponse
? new Annotation[0]
: new Annotation[]{MyDetailView.Factory.get()};
//pojo collection...
MyPagedCollection myCollection = new MyPagedCollection();
//.....
ResponseBuilder builder = Response.ok();
return builder.entity(myCollection, responseAnnotations).build();
}
#GET
#Path("/{id}")
public Response getItem(#PathParam("{id}") String idS, #Context UriInfo uriInfo) {
MyPOJO pojo = ...
Annotation[] responseAnnotations = new Annotation[]{MyDetailView.Factory.get()};
return Response.ok().entity(pojo, responseAnnotations).build();
}
}
After the first test, I tried to use the SelectableEntityFilteringFeature to allow the client to ask for specific fields in the detail, so I changed the ApplicationConfig
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
register(SelectableEntityFilteringFeature.class);
property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
and I've add the "fields" QueryParam to the Resource getItem() method...
#GET
#Path("/{id}")
public Response getDetail(#PathParam({id}) String id,
#QueryParam("fields") String fields,
#Context UriInfo uriInfo) {
....
But as long as I registered the SelectableEntityFilteringFeature class, the EntityFilteringFeature class stopped working. I tried to add "fields" parameter to one of the Resource methods, it worked perfectly. But the MyDetailAnnotation was completely useless.
I tried to register it using a DynamicFeature
public class MyDynamicFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if ("MyResource".equals(resourceInfo.getResourceClass().getSimpleName())
&& "getItem".equals(resourceInfo.getResourceMethod().getName())) {
//*** IS THE CORRECT WAY TO BIND A FEATURE TO A METHOD? ***
//
context.register(SelectableEntityFilteringFeature.class);
context.property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
}
Now the questions:
1) Why registering both the SelectableEntityFilteringFeature feature breaks the EntityFilteringFeature?
2) What is the correct way to bind a feature to a method with the DynamicFeature interface?
Thanks in advance.
This is my first post to Stack Overflow, I hope it was written complaining the rules.
Short answer: you can't. It appears to be a bug as of 2.25.1 and up to 2.26(that I tested with). https://github.com/jersey/jersey/issues/3523
SelectableEntityFilteringFeature implictily registers EntityFilteringFeature (As mentioned here). So I don't see a need to add this.
Since you need Annotation based filtering, you can exclude registering SelectableEntityFilteringFeature.
You can just do,
// Set entity-filtering scope via configuration.
.property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {MyDetailView.Factory.get()})
// Register the EntityFilteringFeature.
.register(EntityFilteringFeature.class)
// Further configuration of ResourceConfig.
You can refer to this example for usage and this example for registering the filter.
So you can remove SelectableEntityFilteringFeature and try just the above mentioned way to register it.

No mapping available for role reference with sessionContext.isCallerInRole()

I have a method that can be called if the person has a specific role and they are associated with a particular group in JIRA. Since the groups in JIRA are dynamic, I can't have a role per JIRA group.
#DeclareRoles({
FileServerRoles.FILE_ADDER,
FileServerRoles.FILE_ADDER_ALL,
FileServerRoles.FILE_VIEWER,
FileServerRoles.FILE_VIEWER_ALL})
public final class FileServerRoles {
/**
* A user that can add files to the system.
*/
public static final String FILE_ADDER = "file-adder";
/**
* A user that can add any files to the system.
*/
public static final String FILE_ADDER_ALL = "file-adder-all";
/**
* A user that can view files in the system.
*/
public static final String FILE_VIEWER = "file-viewer";
/**
* A user that can view all files in the system.
*/
public static final String FILE_VIEWER_ALL = "file-viewer-all";
}
I am declaring all of the roles using #DeclareRoles.
#Decorator
public class FileServerServiceProjectAuthorizationDecorator implements FileServerService {
private static Logger LOGGER = LoggerFactory.getLogger(FileServerServiceProjectAuthorizationDecorator.class);
#Inject
#Delegate
#Any
FileServerService delagate;
#Inject
#CurrentUser
Set<JiraProjectReference> currentUserProjectReferences;
#Resource
SessionContext sessionContext;
void verifyProjectKey(final String projectKey) {
for (final JiraProjectReference projectReference : currentUserProjectReferences) {
if (projectReference.getKey().equalsIgnoreCase(projectKey)) {
return;
}
}
throw new IllegalArgumentException("user not in the project");
}
#RolesAllowed({FileServerRoles.FILE_ADDER, FileServerRoles.FILE_ADDER_ALL})
#Override
public FileAddStatus addFileToRepository(final String projectKey, final String issueKey, final String fileName, final String mimeType, final File file) {
if (!sessionContext.isCallerInRole(FileServerRoles.FILE_ADDER_ALL)) {
verifyProjectKey(projectKey);
}
return delagate.addFileToRepository(projectKey, issueKey, fileName, mimeType, file);
}
#RolesAllowed({FileServerRoles.FILE_VIEWER, FileServerRoles.FILE_VIEWER_ALL})
#Override
public FileDescriptor retrieveFileFromRepository(final String projectKey, final String issueKey, final UUID uuid, final String fileName) {
if (!sessionContext.isCallerInRole(FileServerRoles.FILE_VIEWER_ALL)) {
verifyProjectKey(projectKey);
}
return delagate.retrieveFileFromRepository(projectKey, issueKey, uuid, fileName);
}
}
!sessionContext.isCallerInRole(FileServerRoles.FILE_VIEWER_ALL) always throws IllegalStateException:
Caused by: java.lang.IllegalStateException: No mapping available for role reference file-viewer-all
at com.sun.ejb.containers.EJBContextImpl.isCallerInRole(EJBContextImpl.java:458)
at edu.wvu.esd.swordfish.web.service.FileServerServiceProjectAuthorizationDecorator.retrieveFileFromRepository(FileServerServiceProjectAuthorizationDecorator.java:59)
... 89 more
I have had no problem with any of the roles when that are referenced in #RolesAllowed. I have also tried moving the roles declaration into web.xml. There aren't many references to the error on google.
Has anyone seen this? What was your solution?
I was receiving the same "No mapping available for role reference" error when calling the isCallerInRole(roleName) method within an EJB in GlassFish 3.1. What fixed it for me was adding the appropriate #DeclareRoles annotation to my EJB. If the role name passed to isCallerInRole is not in #DeclareRoles, an IllegalStateException gets thrown. I'm not sure how security works within a decorator but #DeclareRoles was the key for me.
Here is a simple example:
#Stateless
#LocalBean
#DeclareRoles({"user", "admin"})
public class ExampleEJB {
#Resource
private SessionContext sessionContext;
public boolean isUserInRole(String roleName) {
return sessionContext.isCallerInRole(roleName);
}
}