LikeConfigurationSource null exception -> "How to set up XWiki Id "?

Hello.

We decide to use the LikeManager in our extensions to provide some new features.

I’m running my extension on an XWiki 12.10.4 using “xwiki-debug-eclipse project”. The dev server run well and like feature from default flavor works well.

In the pom of my extension I have imported

	<dependency>
		<groupId>org.xwiki.platform</groupId>
		<artifactId>xwiki-platform-like-api</artifactId>
		<version>${platform.version}</version>
	</dependency>

Then in one of my services I inject the LikeManager

@Inject
private LikeManager likeManager;

So far so good eclipse doesn’t complain I can use it in my code.

But when I start my server I have this stacktrace

Caused by: org.xwiki.component.manager.ComponentLookupException: Failed to lookup component [org.xwiki.like.internal.DefaultLikeManager] identified by type [interface org.xwiki.like.LikeManager] and hint [default]
	at org.xwiki.component.embed.EmbeddableComponentManager.getInstance(EmbeddableComponentManager.java:204)
	at org.xwiki.component.embed.EmbeddableComponentManager.getInstance(EmbeddableComponentManager.java:210)
	at org.xwiki.component.embed.EmbeddableComponentManager.getDependencyInstance(EmbeddableComponentManager.java:406)
	at org.xwiki.component.embed.EmbeddableComponentManager.createInstance(EmbeddableComponentManager.java:355)
	at org.xwiki.component.embed.EmbeddableComponentManager.getComponentInstance(EmbeddableComponentManager.java:451)
	at org.xwiki.component.embed.EmbeddableComponentManager.getInstance(EmbeddableComponentManager.java:201)
	... 49 common frames omitted
Caused by: java.lang.IllegalArgumentException: An Entity Reference name cannot be null or empty
	at org.xwiki.model.reference.EntityReference.setName(EntityReference.java:215)
	at org.xwiki.model.reference.EntityReference.<init>(EntityReference.java:187)
	at org.xwiki.model.reference.EntityReference.<init>(EntityReference.java:143)
	at org.xwiki.model.reference.WikiReference.<init>(WikiReference.java:61)
	at org.xwiki.configuration.internal.AbstractDocumentConfigurationSource.getCurrentWikiReference(AbstractDocumentConfigurationSource.java:194)
	at org.xwiki.like.internal.LikeConfigurationSource.getDocumentReference(LikeConfigurationSource.java:55)
	at org.xwiki.configuration.internal.AbstractDocumentConfigurationSource.getCacheKeyPrefix(AbstractDocumentConfigurationSource.java:129)
	at org.xwiki.configuration.internal.AbstractDocumentConfigurationSource.getPropertyValue(AbstractDocumentConfigurationSource.java:339)
	at org.xwiki.configuration.internal.AbstractDocumentConfigurationSource.getProperty(AbstractDocumentConfigurationSource.java:299)
	at org.xwiki.like.internal.DefaultLikeConfiguration.getLikeCacheCapacity(DefaultLikeConfiguration.java:53)
	at org.xwiki.like.internal.DefaultLikeManager.initialize(DefaultLikeManager.java:178)
	at org.xwiki.component.embed.InitializableLifecycleHandler.handle(InitializableLifecycleHandler.java:39)
	at org.xwiki.component.embed.EmbeddableComponentManager.createInstance(EmbeddableComponentManager.java:365)
	at org.xwiki.component.embed.EmbeddableComponentManager.getComponentInstance(EmbeddableComponentManager.java:451)
	at org.xwiki.component.embed.EmbeddableComponentManager.getInstance(EmbeddableComponentManager.java:201)
	... 54 common frames omitted

Am I missing a configuration to use this LikeManager ? It seems relataed to LikeConfigurationSource

Other question :
Also I had to solve a weird issue with this component on tomcat start, so this works but I don’t know why I had to do this manipulation for this Like component and not the others

the class [Lorg/xwiki/like/LikeManager;] couldn't be found in the ClassLoader

I never had this issue with other component, to fix it I have to manually add the like jar to the class path like in the screenshot bellow. Why I have to do that ? My xwiki-platform is builded on 12.10.4 the package for like api is present xwiki-dev-tolls and xwiki-platform are at the same level.

Thanks :slight_smile:

Screenshot 2021-04-22 at 12.17.14

So I think my issue come from this

The LikeConfigurationSource is calling from parent AbstractDocumentConfigurationSource

/**
 * @return the reference pointing to the current wiki
 */
protected WikiReference getCurrentWikiReference()
{
    return new WikiReference(this.wikiManager.getCurrentWikiId());
}

and in my case this.wikiManager.getCurrentWikiId() return NULL.

It’s seems something new in 12.7

    /**
     * @return the reference of the current wiki.
     * @since 12.7RC1
     */
    @Unstable
    default WikiReference getCurrentWikiReference()
    {
        return (getCurrentWikiId() != null) ? new WikiReference(getCurrentWikiId()) : null;
    }
  1. Where should I set up this value ? I guess the value should be “wiki” I use a default xwiki and I have a single wiki ( not a farm )

  2. Maybe LikeConfigurationSource should be enhanced to deal with null value ?

that’s not normal, it means something’s wrong with your XWikiContext: either the context provider returns null (which means it’s not properly injected) or the context does not contain the value which is unlikely.
In either case you’ll have other problems, so you should try to put a breakpoint in DefaultWikiDescriptorManager to see why it returns null.

Thanks for the answer

At the start of the tomcat at the point of injecting these dependencies my XWikiContext is null so getCurrentWikiId() return null.

Screenshot 2021-04-23 at 11.10.51
Screenshot 2021-04-23 at 11.32.38
here stack is null

But I don’t think I have issue with my wiki, if I remove the LikeManager service from my code my XWikiContext is still null while starting tomcat but everyting works well in the wiki even the like feature and I’m using a lot XWikiContext in my code after that and after the initialization while using it in a Resource API for exemple it’s not null anymore.

None other library than LikeConfigurationSource cause that issue to me

The full error path

@Inject
LikeManager likeManager
    @Component
    @Singleton
    public class DefaultLikeManager implements LikeManager, Initializable, Disposable

    @Override
    public void initialize() throws InitializationException
    {
        int likeCacheCapacity = this.likeConfiguration.getLikeCacheCapacity();
    ...
    }

Then it goes to

@Component
@Singleton
public class DefaultLikeConfiguration implements LikeConfiguration
    @Override
    public int getLikeCacheCapacity()
    {
        return this.configurationSource.getProperty("cacheCapacity", 500);
    }
public abstract class AbstractDocumentConfigurationSource extends AbstractConfigurationSource
    implements Initializable, Disposable

    protected String getCacheKeyPrefix()
    {
        return this.referenceSerializer.serialize(getDocumentReference());
    }
@Component
@Singleton
@Named("like")
public class LikeConfigurationSource extends AbstractDocumentConfigurationSource

    @Override
    protected DocumentReference getDocumentReference()
    {
        return new DocumentReference(DOCUMENT_REFERENCE, this.getCurrentWikiReference());
    }

Then the null point exception in getCurrentWikiReference

For me this line seems wrong it should check for null value and use wiki by default I don’t have any initializations issues with any of other component that I inject as start and I use a lot of them. It use a component not yet injected at this moment of the initialization or maybe in some case where it worked that was injected at this moment but only with some luck.

I don’t know what do to, maybe I will have to write my own override of LikeConfigurationSource with this check and my own version LikeManager to use my LikeConfigurationSource. I’ll tell you if it fix it.

So I rewrite theses 3 components with my own changes. I’m not really happy to have to provide my own versions of them just to change a function in LikeConfigurationSource. Is there a way to tell to an external component to inject my version of LikeConfigurationSource instead of rewrite everything ? Because rewriting them cause issue with Like Cache that i have to duplicate.

  • DefaultLikeManager
  • LikeConfiguration
  • LikeConfigurationSource

Anyway. May I suggest a pull requestion for LikeConfigurationSource ?

Replace

    @Override
    protected DocumentReference getDocumentReference()
    {
        return new DocumentReference(DOCUMENT_REFERENCE, this.getCurrentWikiReference());
    }

By

    @Override
    protected DocumentReference getDocumentReference()
    {
    	WikiReference wikiRef = this.wikiManager.getCurrentWikiReference();
    	if(wikiRef == null) {
    		wikiRef = new WikiReference(this.wikiManager.getMainWikiId());
    	}
    	
        return new DocumentReference(DOCUMENT_REFERENCE, wikiRef);
    }

Or if you think the issue really come from my project please help me to setup this CurrentWiki variable. But the Like component is new, I’m not sure someone already tried to integrate it in an other extension ? Can someone try to integrate it in a service in an extension using xwiki-dev-tools to try to reproduce it ? I really double that the issue come from my side, again I never had any issues with other components.

I’m really not sure about your fix here, I’d prefer that you keep investigate on why the context is not properly injected here.
In theory the context is injected quite early in xwiki, the only reason I can imagine why it’s not injected in your case is because your extension needs it and is instantiated very early for some reason. Still not sure why, and that might be the thing to investigate.

Yeah not sure either, and I agree that it’s a brand new thing so maybe you’ll find other problems that we need to fix :slight_smile: just in that specific case I’m really not sure about the real reason of the bug: for me the context should be instantiated at this point.

Maybe you could change the dependency to the LikeManager component in your extension to rely on a Provider<LikeManager> so that you don’t inject it immediately but only when you need to actually use it.

Oh thanks a lot ! It’s what I was looking for

Instead of having

@Inject
private LikeManager likeManager

public List<DocumentReference> getFavoriteWorkspace() {
     UserReference currentUser = userReferenceResolver.resolve(xwikiContextProvider.get().getUserReference());
     List<EntityReference> likedEntities = likeManager.getUserLikes(userRef, 0, 1000);
}

I now have

@Inject
private Provider<LikeManager> likeManager

public List<DocumentReference> getFavoriteWorkspace() {
     UserReference currentUser = userReferenceResolver.resolve(xwikiContextProvider.get().getUserReference());
     List<EntityReference> likedEntities = likeManager.get().getUserLikes(userRef, 0, 1000);
}

And it works well now :slight_smile:

I still don’t know why the other way doesn’t works and the workaround is clean so I won’t dig more becaue injection order are really hard to debug but I’m still thinking that my usage of likeManager was kinda “normal”, it’s the last inject of my service after 10 other injects and I have almost 20 components in my extension which load well and this service is not the first one in the list either

I still think the initialize function of this component should not call this.getCurrentWikiReference() because it may not have been initialized at this moment or it will works by pure luck. I let you discuss my PR idea maybe or keep it in mind if someone else have the issue one day ^^

Maybe an other way to fix it at the Wiki lvl instead could have been to change this code in AbstractDocumentConfigurationSource. Probably a Null Pattern Object or a default xwiki return would have avoid this error. In this piece of code you instantiate an object who can explicitely return null and generate a null pointer exception so I’m not sure about arguments in favor of that. But XWiki is complex so I don’t measure all the consequence of this kind of changes.

    /**
     * @return the reference pointing to the current wiki
     */
    protected WikiReference getCurrentWikiReference()
    {
        return new WikiReference(this.wikiManager.getCurrentWikiId());
    }

Maybe you encoutered the same issue with getMainWiki which deal with the null case ?

    @Override
    public String getMainWikiId()
    {
        XWikiContext xcontext = this.xcontextProvider.get();

        return xcontext != null ? xcontext.getMainXWiki() : "xwiki";
    }

    @Override
    public String getCurrentWikiId()
    {
        XWikiContext xcontext = this.xcontextProvider.get();

        return xcontext != null ? xcontext.getWikiId() : null;
    }

FYI as you can see in my little piece of code I’m accessing XWikiContext in my service to get the current user, and now that it works with Provider I put a breakpoint and at run time the getWikiReference works well and return xwiki

Screenshot 2021-04-23 at 17.12.54