Selective SlingPostProcessors

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Selective SlingPostProcessors

Vidar Ramdal-2
I often write PostProcessors to catch a modification to a resource or
property based on certain criteria.
 I usually know the name of the property I want to act on, and the
resource type of the resource being posted to.
For instance, I want to interfere with a user posting some HTML to a
'html' property on a resource with type "custom-html", to run the HTML
through Tidy or something.

I find myself writing code like this ever-so-often:

public class MyPostProcessor implements SlingPostProcessor {
  public void process(SlingHttpServletRequest, List<Modification> changes) {
    // This PostProcessor should only act on resources with MY_RESOURCE_TYPE
    if (request.getResource().getResourceType() != MY_RESOURCE_TYPE) {
       return;
    }
   // This PostProcessor should only act on property modifications
when the property name is MY_PROPERTY_NAME
    for (Modification change : changes) {
     // Only act on MODIFY
      if (change.getType().equals(ModificationType.MODIFY)) {

        Session session = request.getAttribute("javax.jcr.Session");
       // Only at on Property modification, not Node
        if (session.getItem(change.getSource()).isNode()) {
          continue;
        }
        String propertyName = change.getSource();
        if (propertyName.indexOf("/")>-1) propertyName =
propertyName.substring(propertyName.lastIndexOf("/")+1);
        if (!propertyName.equals(MY_PROPERTY_NAME) {
          continue;
        }
...
and so on.

It appears to me that it would be useful to register a
SlingPostProcesor for only some posts - e.g. when there's a match on
resourceType, property name or other criteria.
These criteria could be specified as scr.properties on the
SlingPostProcessor implementation. SlingPostServlet would then only
pass the matching SlingPostProcessors on to the SlingPostOperation -
or maybe the filtering should be done by the SlingPostOperation.

It strikes me that this would make registering a SlingPostProcessor
somewhat similar to registering a Servlet. Maybe there are some
synergies here.

What do you think? Is this idea worth pursuing?

--
Vidar S. Ramdal <[hidden email]> - http://www.idium.no
Sommerrogata 13-15, N-0255 Oslo, Norway
+ 47 22 00 84 00 / +47 21 531941, ext 2070
Reply | Threaded
Open this post in threaded view
|

Re: Selective SlingPostProcessors

justinedelson
+1 to the registering PostProcessor for specific resource types.

I'm less sure of registering by property name and modification type,  
mostly because you'll still need to iterate through the modification  
list. Also, it seems like there could be ambiguous cases, e.g. If I  
register a PostProcessor with modifiedPropertyNames = [ 'title',  
'description' ], does the PostProcessor get called when only one of  
those properties is called?

Justin

On Jan 14, 2010, at 7:30 AM, Vidar Ramdal <[hidden email]> wrote:

> I often write PostProcessors to catch a modification to a resource or
> property based on certain criteria.
> I usually know the name of the property I want to act on, and the
> resource type of the resource being posted to.
> For instance, I want to interfere with a user posting some HTML to a
> 'html' property on a resource with type "custom-html", to run the HTML
> through Tidy or something.
>
> I find myself writing code like this ever-so-often:
>
> public class MyPostProcessor implements SlingPostProcessor {
>  public void process(SlingHttpServletRequest, List<Modification>  
> changes) {
>    // This PostProcessor should only act on resources with  
> MY_RESOURCE_TYPE
>    if (request.getResource().getResourceType() != MY_RESOURCE_TYPE) {
>       return;
>    }
>   // This PostProcessor should only act on property modifications
> when the property name is MY_PROPERTY_NAME
>    for (Modification change : changes) {
>     // Only act on MODIFY
>      if (change.getType().equals(ModificationType.MODIFY)) {
>
>        Session session = request.getAttribute("javax.jcr.Session");
>       // Only at on Property modification, not Node
>        if (session.getItem(change.getSource()).isNode()) {
>          continue;
>        }
>        String propertyName = change.getSource();
>        if (propertyName.indexOf("/")>-1) propertyName =
> propertyName.substring(propertyName.lastIndexOf("/")+1);
>        if (!propertyName.equals(MY_PROPERTY_NAME) {
>          continue;
>        }
> ...
> and so on.
>
> It appears to me that it would be useful to register a
> SlingPostProcesor for only some posts - e.g. when there's a match on
> resourceType, property name or other criteria.
> These criteria could be specified as scr.properties on the
> SlingPostProcessor implementation. SlingPostServlet would then only
> pass the matching SlingPostProcessors on to the SlingPostOperation -
> or maybe the filtering should be done by the SlingPostOperation.
>
> It strikes me that this would make registering a SlingPostProcessor
> somewhat similar to registering a Servlet. Maybe there are some
> synergies here.
>
> What do you think? Is this idea worth pursuing?
>
> --
> Vidar S. Ramdal <[hidden email]> - http://www.idium.no
> Sommerrogata 13-15, N-0255 Oslo, Norway
> + 47 22 00 84 00 / +47 21 531941, ext 2070
Reply | Threaded
Open this post in threaded view
|

Re: Selective SlingPostProcessors

Vidar Ramdal-2
> On Jan 14, 2010, at 7:30 AM, Vidar Ramdal <[hidden email]> wrote:
>> I often write PostProcessors to catch a modification to a resource or
>> property based on certain criteria.
>> [...]

On Thu, Jan 14, 2010 at 3:40 PM, Justin Edelson <[hidden email]> wrote:
> +1 to the registering PostProcessor for specific resource types.
>
> I'm less sure of registering by property name and modification type, mostly
> because you'll still need to iterate through the modification list.

Yes, but that iteration is often duplicated in postprocessors (in my
case, anyway), so it would be useful to centralize it.

We could introduce a new method (in a new interface extending
SlingPostProcessor):
public void process(Modification modification, List<Modification>
allModifications>, SlingHttpServletRequest req);
The 'modification' param would be the actual modification that this
postprocessor is subscribed to. 'allModifications' would be the same
old modification list. I'm including it because I often inspect other
modifications when deciding what to do with the subscribed
modification.

> Also, it seems like there could be ambiguous cases, e.g. If I register a
> PostProcessor with modifiedPropertyNames = [ 'title', 'description' ], does
> the PostProcessor get called when only one of those properties is called?

We would have to define what to do in those cases. In your example, I
would say the PostProcessor gets called twice, once for title, and
once for description.
If you want your PostProcessor to get called ONLY when BOTH properties
are modified, you could of course just iterate through the
modification list just as before, and return early from process().
But anyway, we need a clear definition.

>> It strikes me that this would make registering a SlingPostProcessor
>> somewhat similar to registering a Servlet. Maybe there are some
>> synergies here.

How about registering PostProcessors on paths, just like we do with
servlets? That would allow for fine-grained registering, like multiple
/postprocessors/resourceType/propertyName registrations, and even
/postprocessors/anyResourceType/propertyName.

--
Vidar S. Ramdal <[hidden email]> - http://www.idium.no
Sommerrogata 13-15, N-0255 Oslo, Norway
+ 47 22 00 84 00 / +47 21 531941, ext 2070
Reply | Threaded
Open this post in threaded view
|

Re: Selective SlingPostProcessors

justinedelson
On Thu, Jan 14, 2010 at 10:03 AM, Vidar Ramdal <[hidden email]> wrote:

> > On Jan 14, 2010, at 7:30 AM, Vidar Ramdal <[hidden email]> wrote:
> >> I often write PostProcessors to catch a modification to a resource or
> >> property based on certain criteria.
> >> [...]
>
> On Thu, Jan 14, 2010 at 3:40 PM, Justin Edelson <[hidden email]>
> wrote:
> > +1 to the registering PostProcessor for specific resource types.
> >
> > I'm less sure of registering by property name and modification type,
> mostly
> > because you'll still need to iterate through the modification list.
>
> Yes, but that iteration is often duplicated in postprocessors (in my
> case, anyway), so it would be useful to centralize it.
>
> We could introduce a new method (in a new interface extending
> SlingPostProcessor):
> public void process(Modification modification, List<Modification>
> allModifications>, SlingHttpServletRequest req);
> The 'modification' param would be the actual modification that this
> postprocessor is subscribed to. 'allModifications' would be the same
> old modification list. I'm including it because I often inspect other
> modifications when deciding what to do with the subscribed
> modification.
>
Ah. I didn't realize you were talking about a new API method. In this
context, property-based registration makes more sense.

But if we're going to change the API, how about replacing List<Modification>
with a more "intelligent" object so you could write code like this:

if (changes.wasPropertyModified(MY_PROPERTY_NAME)) {
 // do something here
}


>  > Also, it seems like there could be ambiguous cases, e.g. If I register a
> > PostProcessor with modifiedPropertyNames = [ 'title', 'description' ],
> does
> > the PostProcessor get called when only one of those properties is called?
>
> We would have to define what to do in those cases. In your example, I
> would say the PostProcessor gets called twice, once for title, and
> once for description.
> If you want your PostProcessor to get called ONLY when BOTH properties
> are modified, you could of course just iterate through the
> modification list just as before, and return early from process().
> But anyway, we need a clear definition.


OK. This will just need to be well-documented (like everything else).


> >> It strikes me that this would make registering a SlingPostProcessor
> >> somewhat similar to registering a Servlet. Maybe there are some
> >> synergies here.
>
> How about registering PostProcessors on paths, just like we do with
> servlets? That would allow for fine-grained registering, like multiple
> /postprocessors/resourceType/propertyName registrations, and even
> /postprocessors/anyResourceType/propertyName.


Somewhat like Servlets, it seems you should be able to register a
PostProcessor for a particular (resource) path using, say,
sling.postprocessor.paths OR a particular resource type using, say,
sling.postprocessor.resourceTypes, but not both at the same time. I don't
think it makes sense to use an artificial path structure like
/postprocessors/foo/bar. resourceType="foo/bar" is much cleaner and more
intuitive. Unlike Servlets, however, if you want a PostProcessor to apply to
*all* resources, you don't need an "anyResourceType" (or
sling/servlet/default) identifier. Simply not having either
sling.postprocessor.paths or sling.postprocessor.resourceTypes set should
indicate the global behavior. This is necessary for backwards compatibility.

Justin