Thanks for your quick reply, Alex!

Alexander Klimetschek writes:
But anyway my gut feeling tells me that you should not make the Resource depending on request parameters or HTTP headers. These should rather define how the resource should be rendered. In your case the resource would most likely simply the table to be addressed. Limits, offsets, and other "query" parameters would then be handled in a servlet that provides a query rendering of this resource/db table. That would most likely make that servlet and the resource implementation generic for all tables, but you could still have specific overrides for special tables (by using a more specific resource type and a "generic/table" super resource type). In your Resource implementation you could implement adaptTo to elegantly get to the underlying JDBC connection or whatever the raw database access it and use that in your Database query servlet.

Interesting idea! My initial implementation fetches the data from the table in the custom ResourceResolver and place it in a table-specific Resource implementation instance, but it sounds like you're suggesting instead to place the means of fetching the data into the Resource and let the rendering servlet actually do the fetching. By deferring the actual fetch, I can let the rendering servlet inspect the request for filters and query parameters and have it fetch based on those params.

For example, let's say I want to map a User table to /prefix/users. For JsonRestStore to work, calling /prefix/users should return a JSON array of user objects and calling /prefix/users/id should return a single JSON object representing the user with the specified ID. In addition, requesting /prefix/users?active=true should return a list of users in an "active" state (as defined by my application). I would create a UserResourceProvider class that inspects the path to determine whether a list of users or a single user has been requested. For a list of users, it would return a UserResource with a type of "myapp/user-list". For a single user, it would return a UserResource of type "myapp/user" with a resource metadata key set to the user ID parsed off the path. The UserResource object would implement adaptTo(MyAppDAO.class) where MyAppDAO is the data access object used to fetch User objects.

I'm using Groovy scripts for rendering in this application, so to render a GET request for the user list I would create /apps/myapp/user- list/GET.groovy. Inside that script, I can use the predefined request variable to look at the Content-Range header and any request parameters, then call my DAO that was adapted from the resource:

  // pseudocode
  def active = request.getParameter("active")
  def dao = resource.adaptTo(MyAppDAO.class)

  def users = []
  if (active != null) {
    users = dao.findUsersByActiveState(active)
  }
  else {
    users = dao.allUsers()
  }

  // now render each user as JSON...

I would also need to implement POST.groovy to fully support the JsonRestStore, but that is trivial since I have access to the MyAppDAO object in the script. The scripts for GET, POST, PUT, and DELETE for a "myapp/user" object would get the MyAppDAO object the same way, but in addition would pull the user ID out of the resource metadata to determine which user to act upon.

My only concern with this setup is that I feel like I'm not leveraging all of the existing capabilities of Sling. For example, instead of rendering JSON myself, it would be nice for my Resource subclass supported adaptTo(ValueMap.class) and use the "json" selector. Also, I expect that I would be able to save and delete instances of objects if I supported adaptTo(PersistableValueMap.class), though I haven't tried this yet. Your suggestion is great and it solves my immediate problem, but it feels like I'm bypassing a lot of functionality I could otherwise get for free.

Thanks again!

p.

Reply via email to