Hi Olaf, I'd like to dive into your last comment some further. "You can never re-provide a resource resolved by a different resource provider". In my case, I don't think I'm re-providing, I'm just providing - if anything, I'm delegating to the resource resolving mechanism which ends up at my ResourceProvider. So I'm not ending up at a different resource provider at this point, but actually the same resource provider.
It's still not entirely clear to me conceptually speaking why this is a bad practise. Best regards 2017-06-08 23:34 GMT+02:00 Olaf <o...@x100.de>: > Hi Bart, > > I put them right in the code, where they are probably hard to spot :-) > > The essential one should be the one next to the place where you add the > resource to the list of resource in ResourceProvider#listChildren. Updated > version: > > You can never re-provide a resource resolved by a different resource > provider: A resource has resource meta-data containing, amongst others, the > resolution path. Also, a resource is always tied to its resource resolver > via resource#getResourceProvider(). Thus, you must create a new > (Synthetic) > resource here and add it to the list. For instance, you could extend > SyntheticResource to create your own resource wrapper, and delegate > Resource#adaptTo to your wrapped resource. > > > Kind regards, > Olaf > > -----Original Message----- > From: Bart Wulteputte [mailto:bart.wultepu...@gmail.com] > Sent: Donnerstag, 8. Juni 2017 23:31 > To: users@sling.apache.org > Subject: Re: Resource provided by custom ResourceProvider crashes upon > calling hasChildren() > > Hi Olaf, > > Is it possible that you didn't send your remarks? I don't see them in your > first mail. > > Best regards > > > _____________________________ > From: Olaf <o...@x100.de<mailto:o...@x100.de>> > Sent: donderdag, juni 8, 2017 11:25 PM > Subject: RE: Resource provided by custom ResourceProvider crashes upon > calling hasChildren() > To: <users@sling.apache.org<mailto:users@sling.apache.org>> > > > Hi Bart, > > I just saw you already used ResourceProvider#(ResolveContext ctx, Resource > parent), excellent. Forget my first remark then, providing a > SyntheticResource should do. > > Cheers, > Olaf > > -----Original Message----- > From: Olaf [mailto:o...@x100.de] > Sent: Donnerstag, 8. Juni 2017 23:23 > To: users@sling.apache.org<mailto:users@sling.apache.org> > Subject: RE: Resource provided by custom ResourceProvider crashes upon > calling hasChildren() > > Hi Bart! > > Resource providers are mighty, but tricky things indeed. Please find my > >remarks below. > > Cheers, > Olaf > > -----Original Message----- > From: Bart Wulteputte [mailto:bart.wultepu...@gmail.com] > Sent: Donnerstag, 8. Juni 2017 22:50 > To: users@sling.apache.org<mailto:users@sling.apache.org> > Subject: Resource provided by custom ResourceProvider crashes upon calling > hasChildren() > > 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); > } > > > > Resources must never provide their own children - this is the resource > provider's responsibility, see ResourceProvider#listChildren( > ResolveContext > ctx, Resource parent). The reason is that resource providers can be nested, > i.e. the child of a resource may be provided by a different resource > provider. Thus, the code below should be situated in the before mentioned > method of your resource provider. > > @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) { > > > > You can never re-provide a resolved resource: A resource has resource > meta-data containing, amongst others, the resolution path. Also, a resource > is always tied to its resource resolver via resource#getResourceProvider() > . > Thus, you must create a new (Synthetic) resource here and add it to the > list. For instance, you could extend SyntheticResource to create your own > resource wrapper, and delegate Resource#adaptTo to your wrapped resource. > > 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(ResourceMetadat > a.java:367) > at > org.apache.sling.api.resource.ResourceMetadata.put( > ResourceMetadata.java:379 > ) > at > org.apache.sling.api.resource.ResourceMetadata. > setResolutionPath(ResourceMet > adata.java:276) > at > org.apache.sling.resourceresolver.impl.helper. > UniqueResourceIterator.seek(Un > iqueResourceIterator.java:51) > at > org.apache.sling.resourceresolver.impl.helper. > UniqueResourceIterator.seek(Un > iqueResourceIterator.java:30) > at > org.apache.sling.resourceresolver.impl.helper. > AbstractIterator.hasNext(Abstr > actIterator.java:33) > at > org.apache.sling.resourceresolver.impl.helper. > ResourceIteratorDecorator.hasN > ext(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? > > > > > >