Hi,

While trying to implement a POC and I ran into a strange error, and I hope
you guys can help identify whether this is a bug, or if I'm just doing
something wrong.

I'm playing around with custom resource providers to pull external data
into sling. I don't do anything crazy in there (yet), but it's just as a
poc. I want to combine it with custom resource implementations as well.

My resource provider is registered on path "/content/data" and basically
handles anything on this path. Currently I'm just building a virtual data
structure. Inside this virtual structure some paths will have a very
specific ResourceProvider implementation tied to said path which retrieve
the resource info from an external system when calling listChildren. So for
example "/content/data/2001/external/app" could have a more specific
resource provider registered here (which should work based on provider
priority). Unfortunately, I can't seem to get part 1 (building the virtual
structure) working without some hacks.

*My resource provider implementation looks like this:*

@Override
public Resource getResource(@Nonnull final ResolveContext
resolveContext, @Nonnull final String path, @Nonnull final
ResourceContext resourceContext, final Resource parent) {
    return new SyntheticResource(resolveContext.getResourceResolver(),
path, SyntheticResource.RESOURCE_TYPE_NON_EXISTING);
}

@Override
public Iterator<Resource> listChildren(@Nonnull final ResolveContext
resolveContext, @Nonnull final Resource resource) {
    final ResourceResolver resourceResolver =
resolveContext.getResourceResolver();
    final List<Resource> list = new ArrayList<>();
    // search data basically returns a list of child paths
    // e.g. /content/data/2000, /content/data/2001, ...
    // since these are 'children' the resolving ends up in this
ResourceProvider
    // which yields a new SyntheticResource on the given path (for now)
    for (String path : searchData.childrenOf(resource)) {
        final Resource childRes = resourceResolver.getResource(path);
        if (childRes != null) {
            list.add(childRes);
        }
    }
    return list.isEmpty() ? null : list.iterator();
}



*The code producing my error:*

Resource r=resourceResolver.getResource("/content/data");
r.hasChildren();


*My Error:*

java.lang.UnsupportedOperationException: ResourceMetadata is locked
at
org.apache.sling.api.resource.ResourceMetadata.checkReadOnly(ResourceMetadata.java:367)
at
org.apache.sling.api.resource.ResourceMetadata.put(ResourceMetadata.java:379)
at
org.apache.sling.api.resource.ResourceMetadata.setResolutionPath(ResourceMetadata.java:276)
at
org.apache.sling.resourceresolver.impl.helper.UniqueResourceIterator.seek(UniqueResourceIterator.java:51)
at
org.apache.sling.resourceresolver.impl.helper.UniqueResourceIterator.seek(UniqueResourceIterator.java:30)
at
org.apache.sling.resourceresolver.impl.helper.AbstractIterator.hasNext(AbstractIterator.java:33)
at
org.apache.sling.resourceresolver.impl.helper.ResourceIteratorDecorator.hasNext(ResourceIteratorDecorator.java:45)
...


*My analysis so far:*

Because I use resourceResolver.getResource() inside the listChildren method
of my custom resource provider, I pass through some internal resolving
which do some decorating on the resource and the iterators. This in itself
is not a problem, but one of those decorators (the
ResourceDecoratorTracker) locks the ResourceMedatadata object - which is a
problem as other decorators like ResourceIteratorDecorator try to update
data (in this case it tries to set/update the resolutionPath of the fetched
resource's ResourceMetadata to the path of said resource during the
execution of 'next()'  - which seems a tad odd). And here we end up trying
to modify a locked object.


This feels like a bug. I can get around it by creating my own
ResourceMetadata class which extends ResourceMetadata and overrides the
lock() method to do nothing and passing that to the SyntheticResource upon
creation. But this feels like hacking. Second option: I don't use the
resourceResolver to get the resource, but instead create new
SyntheticResource objects in the listChildren of my ResourceProvider
directly (in the same way i would do 'getResource').

Preferably I want to pass through the appropriate ResourceProvider (since
the intend is to have more specific resource providers mounted on paths
inside this virtual structure). In theory this should work, and in practise
it does as well (if I hack it a bit as described before).

So the main question is, can I do this in a non hackish way? And is this a
bug?

Reply via email to