404 ModifiableValueMap Not Found: Considerations in Using ModifiableValueMaps in an API

Published on by Dan KlcoPicture of Me Dan Klco

So let's just say you are designing an API which allows both reading and writing of attributes for a Resource.  Of course, you want to use consistent code to persist the values, so you use the (relatively) new CRUD features in the Sling API to both get and set the property values.  To save code, you might use a ModifiableValueMap for both operations, which works great in author as an administrative user.

The Problem

At some point, you'll put your code in publish or test with a user with limited permissions and then: the dreaded null pointer. Of course, at this point, you need to track down the line throwing the exception and unexpectedly the NPE is coming from accessing the ModifiableValueMap. How can that be? The Resource exists and you can get a ValueMap, why is the ModifiableValueMap not working?  The key lies in the JcrNodeResource's adaptTo method:
} else if (type == ModifiableValueMap.class ) {
// check write
try {
getNode().getSession().checkPermission(getPath(), "set_property");
return (Type) new JcrModifiableValueMap(getNode(), this.dynamicClassLoader);
} catch (AccessControlException ace) {
// the user has no write permission, cannot adapt
LOGGER.debug("adaptTo(ModifiableValueMap): Cannot set properties on {}", this);
} catch (RepositoryException e) {
// some other problem, cannot adapt
LOGGER.debug("adaptTo(ModifiableValueMap): Unexpected problem for {}", this);

Here, it's checking to see if the user can write before attempting to return the ModifiableValueMap and if not, just returning null. 

The Solution

So how do we get around this? There are two options:

  • Create two variables, a ValueMap for reading and a ModifiableValueMap for writing
  • Create one variable and set it to a ValueMap if the adaptation to a ModifiableValueMap fails

Personally, I would recommend using the latter and setting the variable to a ValueMap if the attempt to adapt the Resource to a ModifiableValueMap returns null.  Attempts to call put methods will throw an UnsupportedOperationException which is at least somewhat frendlier than a NullPointerException. An alternative, would be to check if the ModifiableValueMap is not null in every set method and return an appropriate exception.

Thanks to Matthieu Tremblay for pointing out this issue.


comments powered by Disqus