Message Boards

Null service @Reference...but the component's reference says SATISFIED

thumbnail
Enrique Valdes Lacasa, modified 4 Years ago.

Null service @Reference...but the component's reference says SATISFIED

Junior Member Posts: 92 Join Date: 7/29/14 Recent Posts
Good day Liferay Community.

Any help will be greatly appreciated - thank you beforehand.

I created a component consisting of a POJO class like this:
@Component(
        immediate = true,
        service = MemberSearchContainer.class
    )
    public class MemberSearchContainer {   
    
    public MemberSearchContainer () {   }
    
    [...]
    
        @Reference
        private MemberLocalService mls;
    }

All looks good when deploying the bundle - just the regular INFO messages.

I connect to Apache GoGo , and look for the bundle's info:
g! b 1081
Id=1081, Status=ACTIVE
"Registered Services"
[...]
{xxx.member.portlet.model.MemberSearchContainer}={service.id=13234, service.bundleid=1081, service.scope=bundle, component.name=xxx.portal.member.portlet.model.MemberSearchContainer, component.id=5716}
[...]

And after that I look for information about the component doing:
g! scr:info 5716
    Component Description: xxx.portal.member.portlet.model.MemberSearchContainer
    ==================================================================================
    Class:        xxx.portal.member.portlet.model.MemberSearchContainer
    Bundle:        1081 (xxx.portal.member.admin:0.1.0.SNAPSHOT)
    Enabled:       true
    Immediate:     true
    Services:      [xxx.portal.member.portlet.model.MemberSearchContainer]
    Scope:         singleton
    Config PID(s): [xxx.portal.member.portlet.model.MemberSearchContainer], Policy: optional
    Base Props:    (0 entries)
    
    Component Configuration Id: 5716
    --------------------------------
    State:        ACTIVE
    Service:      13234 [xxx.portal.member.portlet.model.MemberSearchContainer]
    Config Props: (2 entries)
      component.id<long> = 5716
      component.name<string> = xxx.portal.member.portlet.model.MemberSearchContainer
    References:   (total 1)
      - mls: xxx.portal.member.service.MemberLocalService SATISFIED 1..1 static
        target=(*) scope=bundle (1 binding):
        * Bound to [13044] from bundle 1079 (xxx.portal.member.service:0.1.0.SNAPSHOT)
</string></long>

So in the "References" section we can see that indeed the MemberLocalService reference is actually SATISFIED.

But...when I debug the component, I notice that the MemberLocalService mls variable is null...so it's not being injected, and I'm not sure why. I tried many things and can't understand what's going on.

By the way - in a portlet component within the same bundle, when I use this:

@Reference
    private MemberLocalService mls;

The service gets injected without a problem.

I'm very intrigued to understand what's happening. Any feedback will be of great help.

Thanks a lot!

Best regards,
Enrique
thumbnail
Enrique Valdes Lacasa, modified 3 Years ago.

RE: Null service @Reference...but the component's reference says SATISFIED

Junior Member Posts: 92 Join Date: 7/29/14 Recent Posts
All right, I'll answer myself!

I went past the issue...but I feel I could still have a better understanding about the problem. Since I am learning OSGi, I guess I wasn't paying close attention to the bedrocks.

I forgot to mention the way I was instantiating MemberSearchContainer (since it was a long post already). The way I was doing it, was by using a plain old custom parameterized Java constructor, like:

MemberSearchContainer msc = new MemberSearchContainer(...);


BUT since OSGi takes care of the injection, my references in the POJO were null because (I think) I was the one instantiating the object...NOT OSGi.

So instead of doing that, in the portlet class, I just did:

@Reference
private MemberSearchContainer _msc;


And once I did that, my _msc variable had all the references piped in via OSGi!

The thing that seems confusing is that OSGi just makes use of an empty constructor...so you get your instance, but it won't have taken in any parameters. I wrote a getMemberSearchConstructor getter and make it return MemberSearchContainer.

I'm glad I went past it! Any light on the subject and if there is a best practice to improve the instantiation will still be appreciated.

Thanks!
thumbnail
Christoph Rabel, modified 3 Years ago.

RE: Null service @Reference...but the component's reference says SATISFIED

Liferay Legend Posts: 1554 Join Date: 9/24/09 Recent Posts
The runtime can't really call non-empty constructors. It would have to detect all constructors, determine the parameters and somehow find a way to add them. While this is certainly possible, it makes the process highly complicated.
Also, all constructors with unknown parameters must be discarded. This would make the system quite error prone because you would never really know, which constructor is called.
Basically: You should not instantiate components directly, you should always reference them. If it was more convenient (for whatever reasons) to you to instantiate the class yourself (e.g. in a factory)
MemberSearchContainer msc = new MemberSearchContainer(...);

then you have to call setMemberLocalService yourself.

For better understanding of OSGI I can really recommend the osgi course by Olaf Kock:
https://university.liferay.com/osgi-basics
thumbnail
Enrique Valdes Lacasa, modified 3 Years ago.

RE: Null service @Reference...but the component's reference says SATISFIED

Junior Member Posts: 92 Join Date: 7/29/14 Recent Posts
Thanks a lot for the feedback Cristoph!
It makes more sense after reading your message.  And I'll check out the course!
thumbnail
Enrique Valdes Lacasa, modified 3 Years ago.

RE: Null service @Reference...but the component's reference says SATISFIED

Junior Member Posts: 92 Join Date: 7/29/14 Recent Posts
Hello again everyone!

I watched the course, Christoph; it was informative, thanks!

I got to a new question related to this...hopefully someone can shed some light.

This time is about using OSGi references within [ENTITY_NAME]Impl classes (the model implementation classes generated in Service Builder; Semoticon. Let's say I have a SB entity called Member and want to have some logic in my MemberImpl class to use some services. For example:

@Reference
private UserLocalService _uls;


In a Member document contributor wehre I index some search fields...I notice that member instances have _uls as null (in the same way as it was null whenever I manually instantiated the search container from my original question). I gave a look to how Liferay deals with this within its [ENTITY_NAME]Impl classes in its source code...but for example in UserImpl, they use all xxxLocalServiceUtils around (which are supposed to be non-preferred when working in OSGi).

So is there a recommended approach to use OSGI in [ENTITY_NAME]Impl classes? Is OSGi appropriate to use in SB [ENTITY_NAME]Impl classes at all? I browsed through the documentation but couldn't get any ideas out of it.

Thanks in advance!
thumbnail
Enrique Valdes Lacasa, modified 3 Years ago.

RE: Null service @Reference...but the component's reference says SATISFIED

Junior Member Posts: 92 Join Date: 7/29/14 Recent Posts
I made a finding in respect to model implementations. So besides sharing it, I'm going to give more background on what I meant with my last message.

So in my Member document contributor (which is an OSGi component):

@Component(
        immediate = true,
        property = "indexer.class.name=xxx.Member",
        service = ModelDocumentContributor.class
)
public class MemberModelDocumentContributor implements ModelDocumentContributor<member> {

    @Override
    public void contribute(Document document, Member member) {

        document.addNumber(Field.COMPANY_ID, member.getCompanyId());
        document.addNumber(Field.GROUP_ID, member.getGroupId());
        document.addNumber(Field.SCOPE_GROUP_ID, member.getGroupId());
        ...
        // more field additions to search document
        ...
    }
    
}</member>


If I add some logic using OSGi references in my MemberImpl class:

@Component(
    immediate = true,
    service = Member.class
)
public class MemberImpl extends MemberBaseImpl {
    /*
    * NOTE FOR DEVELOPERS:
    *
    * Never reference this class directly. All methods that expect a member public profile model instance should use the {@link org.cbdce.portal.member.model.MemberPublicProfile} interface instead.
    */
	public MemberImpl() {
	}
	
	[my custom model methods]
	
	@Reference
	private UserLocalService _uls;
	
	}


...the Member object that gets passed within the contribute method (the "member" parameter)...is not an OSGi object, and so _uls is going to be null.

I realized a possible workaround...although it feels wrong to me...but maybe it's the way to go. Again, any feedback = greatly appreciated!

If I create my own reference of a Member in my document contributor...like this:

@Reference
    private Member _member;
    

and then, I noticed there is a method within model entities called getModelAttributes() (inherited from BaseModel class)...and at the same time, a setModelAttributes(). So using these methods I can do this:

@Component(
        immediate = true,
        property = "indexer.class.name=xxx.Member",
        service = ModelDocumentContributor.class
)
public class MemberModelDocumentContributor implements ModelDocumentContributor<member> {

    @Override
    public void contribute(Document document, Member member) {

        _member.setModelAttributes(member.getModelAttributes());

        document.addNumber(Field.COMPANY_ID, _member.getCompanyId());
        document.addNumber(Field.GROUP_ID, _member.getGroupId());
        document.addNumber(Field.SCOPE_GROUP_ID, _member.getGroupId());
        ...
        // more field additions to search document
        ...
    }
    
    @Reference
    private Member _member;

}
</member>

Now my _member reference has the reference  (userLocalService) injected correctly AND all the required "member" model attributes (id, the companyId, groupId, etc...everything).

Does this seem like a good way to go? I'm concerned that Member instances wouldn't be initialized as OSGi references somewhere else and would get NullPointerException...although it could be a suitable workaround for my situation.

Thanks!!!