Resource provided by custom ResourceProvider crashes upon calling hasChildren()

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Resource provided by custom ResourceProvider crashes upon calling hasChildren()

Bart Wulteputte
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 | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

RE: Resource provided by custom ResourceProvider crashes upon calling hasChildren()

Olaf Otto
Hi Bart!

Resource providers are mighty, but tricky things indeed. Please find my >remarks below.

Cheers,
Olaf

-----Original Message-----
From: Bart Wulteputte [mailto:[hidden email]]
Sent: Donnerstag, 8. Juni 2017 22:50
To: [hidden email]
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(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 | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

RE: Resource provided by custom ResourceProvider crashes upon calling hasChildren()

Olaf Otto
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:[hidden email]]
Sent: Donnerstag, 8. Juni 2017 23:23
To: [hidden email]
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:[hidden email]]
Sent: Donnerstag, 8. Juni 2017 22:50
To: [hidden email]
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(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 | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Resource provided by custom ResourceProvider crashes upon calling hasChildren()

Bart Wulteputte
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 <[hidden email]<mailto:[hidden email]>>
Sent: donderdag, juni 8, 2017 11:25 PM
Subject: RE: Resource provided by custom ResourceProvider crashes upon calling hasChildren()
To: <[hidden email]<mailto:[hidden email]>>


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:[hidden email]]
Sent: Donnerstag, 8. Juni 2017 23:23
To: [hidden email]<mailto:[hidden email]>
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:[hidden email]]
Sent: Donnerstag, 8. Juni 2017 22:50
To: [hidden email]<mailto:[hidden email]>
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(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 | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

RE: Resource provided by custom ResourceProvider crashes upon calling hasChildren()

Olaf Otto
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:[hidden email]]
Sent: Donnerstag, 8. Juni 2017 23:31
To: [hidden email]
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 <[hidden email]<mailto:[hidden email]>>
Sent: donderdag, juni 8, 2017 11:25 PM
Subject: RE: Resource provided by custom ResourceProvider crashes upon
calling hasChildren()
To: <[hidden email]<mailto:[hidden email]>>


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:[hidden email]]
Sent: Donnerstag, 8. Juni 2017 23:23
To: [hidden email]<mailto:[hidden email]>
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:[hidden email]]
Sent: Donnerstag, 8. Juni 2017 22:50
To: [hidden email]<mailto:[hidden email]>
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?





Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Resource provided by custom ResourceProvider crashes upon calling hasChildren()

Bart Wulteputte
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 <[hidden email]>:

> 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:[hidden email]]
> Sent: Donnerstag, 8. Juni 2017 23:31
> To: [hidden email]
> 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 <[hidden email]<mailto:[hidden email]>>
> Sent: donderdag, juni 8, 2017 11:25 PM
> Subject: RE: Resource provided by custom ResourceProvider crashes upon
> calling hasChildren()
> To: <[hidden email]<mailto:[hidden email]>>
>
>
> 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:[hidden email]]
> Sent: Donnerstag, 8. Juni 2017 23:23
> To: [hidden email]<mailto:[hidden email]>
> 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:[hidden email]]
> Sent: Donnerstag, 8. Juni 2017 22:50
> To: [hidden email]<mailto:[hidden email]>
> 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?
>
>
>
>
>
>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

RE: Resource provided by custom ResourceProvider crashes upon calling hasChildren()

Olaf Otto
Hi Bart

Perhaps this would be a good one for someone from the ASF to provide more details.

From my point of view, if a resource provider is in fact providing a resource, be it via listChildren or getResource, it must be the sole source of that resource, i.e. no other resource provider must have resolved these resources as this would essentially break the architecture, in which resource provider hierarchies are managed by sling, not the providers themselves and there is a 1:1 relationship between resources and their providers.

The fact calling resourceResolver.getResource(path); ends up in your resource provider is coincidental in this context, for it could be any other resource provider at runtime. The rule basically is that the resource and it's meta data must be new when returned by your provider.

Kind regards,
Olaf

-----Original Message-----
From: Bart Wulteputte [mailto:[hidden email]]
Sent: Freitag, 9. Juni 2017 09:36
To: [hidden email]
Subject: Re: Resource provided by custom ResourceProvider crashes upon calling hasChildren()

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 <[hidden email]>:

> 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:[hidden email]]
> Sent: Donnerstag, 8. Juni 2017 23:31
> To: [hidden email]
> 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 <[hidden email]<mailto:[hidden email]>>
> Sent: donderdag, juni 8, 2017 11:25 PM
> Subject: RE: Resource provided by custom ResourceProvider crashes upon
> calling hasChildren()
> To: <[hidden email]<mailto:[hidden email]>>
>
>
> 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:[hidden email]]
> Sent: Donnerstag, 8. Juni 2017 23:23
> To: [hidden email]<mailto:[hidden email]>
> 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:[hidden email]]
> Sent: Donnerstag, 8. Juni 2017 22:50
> To: [hidden email]<mailto:[hidden email]>
> 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?
>
>
>
>
>
>

Loading...